在运行时动态设置XML URI - XSLT选项

时间:2016-03-14 23:33:01

标签: java xml xslt namespaces jaxb

我有许多Java类,我正在与JAXB一起使用以生成XML。 java类每年都有很小的变化,但输出XML需要对它进行非常具体的年度更改,并且它有点难以捉摸。我已尝试使用DOM更新属性,但沿着树的节点仍在维持先前的状态。我尝试使用反射在编组之前直接更新注释,但它似乎没有达到预期的效果。我也尝试用本地类替换XMLRootElement对象(和XMLType,XMLElement),但似乎没有任何工作正常,因为有些信息似乎总是保留在某处,即使我似乎已经更改了成员/属性/等。 / p>

我不打算每年复制所有java对象,以便我可以更改名称空间以符合要求。

现在,我认为XSLT可能是最后一个选择,但我几乎不知道它。有没有一种简单的方法来更新位于根元素上的5-8名称空间URI?我不想更改前缀(它们已经使用前缀映射器设置),我只想将命名空间从“com.help.me.2014”更改为“com.help.me.2015”。

由于 安迪

解决:

首先,我非常感谢努力和回应。我没有尝试过任何一个,因为在回到它们之前我想出了一个不同的解决方案。

将来出现的任何人都可以尝试下面列出的项目(作为XSLT解决方案),或者您可以尝试我在下面描述的内容。

我以两种不同的样式/格式生成XML,一种带有SOAP封装器,另一种带有SOAP封装器。由于我很难访问DOM / SOAP对象中的实际命名空间,并且无法在运行时更改注释,因此我最终捕获了输出流并操纵了结果字符串。

SOAP:

ByteArrayOutputStream stream = new ByteArrayOutputStream(); soapMessage.writeTo(stream); String file = new String(stream.toByteArray);

...操纵文件(现在是一个字符串),替换值等 - >实际上传递给依赖注入的转换器,然后通过response.write

发送到客户端

JAXB Marshalling与SOAP非常相似,它们都将生成的String发送到转换器上,转换器将其作为StringBuilder进行操作,然后将其发送。

再次感谢您的建议。希望它能在将来帮助某个人,尽管这个要求有点不足。

安迪

2 个答案:

答案 0 :(得分:0)

每年更改名称空间几乎肯定是错误的,但以下XSLT样式表将更改名称空间

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:old="oldspace" 
    version="1.0">

    <xsl:template match="old:*">
        <xsl:element name="{local-name(.)}" namespace="newspace">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@old:*">
        <xsl:attribute name="{local-name()}" namespace="newspace">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

    <xsl:template match="*">
        <xsl:copy select=".">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:copy-of select="."/>
    </xsl:template>

    <xsl:template match="processing-instruction()|comment()">
        <xsl:copy-of select="."/>
    </xsl:template>

</xsl:stylesheet>

此样式表会在适当时创建将名称空间从 oldspace 更改为新闻空间的每个元素的副本。除了名称空间更改之外,原始文档也会保留。可以为需要更改的每个命名空间创建类似的模板(请注意,有两个特定于命名空间的模板)。

请注意,前缀将被更改。这些并不是真正的内容,因此在这种情况下几乎不可能保留它们。我能想到保留这些内容的唯一方法是为原始文件中的每个元素编写一个单独的模板,直接创建新元素而不是使用xsl:element元素。

例如,给定的xml

<os:myroot xmlns:os="oldspace">
    <?keep-this?>
    <os:testing abc='3' def='9'>
        <!-- This is a child -->
        <os:item>1234</os:item>
    </os:testing>
    <!-- this element is in the default namespace -->
    <testing2>
        <abc>112233</abc>
    </testing2>
</os:myroot>

转换为

<myroot xmlns="newspace">
    <?keep-this?>
    <testing>
        <!-- This is a child -->
        <item>1234</item>
    </testing>
    <!-- this element is in the default namespace -->
    <testing2 xmlns="">
        <abc>112233</abc>
    </testing2>
</myroot>

其中 oldspace 命名空间中的所有元素现在都位于新闻空间命名空间中。

答案 1 :(得分:0)

这是一个允许您以xsl:param s的形式传递旧的和新的名称空间URI的选项。

XML输入(借用Matthew的回答;谢谢!)

<os:myroot xmlns:os="com.help.me.2014">
    <?keep-this?>
    <os:testing abc='3' def='9'>
        <!-- This is a child -->
        <os:item>1234</os:item>
    </os:testing>
    <!-- this element is in the default namespace -->
    <testing2>
        <abc>112233</abc>
    </testing2>
</os:myroot>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="oldns" select="'com.help.me.2014'"/>
  <xsl:param name="newns" select="'com.help.me.2015'"/>

  <xsl:template match="@*|node()" name="ident">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" priority="1">
    <xsl:choose>
      <xsl:when test="namespace-uri()=$oldns">
        <xsl:element name="{name()}" namespace="{$newns}">
          <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="{name()}" namespace="{namespace-uri()}">
          <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

XML输出

<os:myroot xmlns:os="com.help.me.2015"><?keep-this?>
   <os:testing abc="3" def="9"><!-- This is a child -->
      <os:item>1234</os:item>
   </os:testing><!-- this element is in the default namespace -->
   <testing2>
      <abc>112233</abc>
   </testing2>
</os:myroot>

这是一个产生相同输出的XSLT 2.0选项......

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="oldns" select="'com.help.me.2014'"/>
  <xsl:param name="newns" select="'com.help.me.2015'"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" priority="1">
    <xsl:element name="{name()}" namespace="{
      if (namespace-uri()=$oldns) then $newns else namespace-uri()}">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

这是另一个处理多个命名空间uris的2.0示例。旧的和新的uris以逗号作为分隔符的字符串传递。

uris的顺序很重要。第一个旧的uri对应于第一个新的uri。第二个老uri对应第二个新uri。等

XML输入(已更新为具有多个名称空间uri)

<os:myroot xmlns:os="com.help.me.2014">
    <?keep-this?>
    <os:testing abc='3' def='9'>
        <!-- This is a child -->
        <os:item>1234</os:item>
    </os:testing>
    <!-- this element is in the default namespace -->
    <testing2>
        <abc>112233</abc>
    </testing2>
    <os2:testing xmlns:os2="com.help.me.again.2014">
        <os2:item>ABCD</os2:item>
    </os2:testing>
</os:myroot>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="oldns" select="'com.help.me.2014,com.help.me.again.2014'"/>
  <xsl:param name="newns" select="'com.help.me.2015,com.help.me.again.2015'"/>

  <xsl:variable name="oldns-seq" select="tokenize($oldns,',')"/>
  <xsl:variable name="newns-seq" select="tokenize($newns,',')"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" priority="1">
    <xsl:variable name="nsIdx" select="index-of($oldns-seq,namespace-uri())"/>
    <xsl:element name="{name()}" namespace="{
      if (namespace-uri()=$oldns-seq) then $newns-seq[$nsIdx] else namespace-uri()}">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

XML输出

<os:myroot xmlns:os="com.help.me.2015"><?keep-this?>
   <os:testing abc="3" def="9"><!-- This is a child -->
      <os:item>1234</os:item>
   </os:testing>
   <!-- this element is in the default namespace -->
   <testing2>
      <abc>112233</abc>
   </testing2>
   <os2:testing xmlns:os2="com.help.me.again.2015">
      <os2:item>ABCD</os2:item>
   </os2:testing>
</os:myroot>