简体   繁体   English

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

[英]Dynamically set XML URI at runtime - XSLT option

I have a number of Java classes that I am using in conjunction with JAXB in order to generate XML. 我有许多Java类与JAXB结合使用,以生成XML。 The java classes have minimal changes from year to year but the output XML needs to have very specific yearly changes to it and it's proving a little elusive. Java类每年之间的更改很小,但是输出XML需要对其进行非常具体的年度更改,事实证明这有点难以捉摸。 I've tried updating the attributes using DOM but nodes further along the tree are maintaining the previous state. 我尝试使用DOM更新属性,但沿树的节点仍保持先前的状态。 I've tried using reflection to update the annotations directly before marshalling but it doesn't seem to be having the desired effect. 我尝试使用反射在编组之前直接更新批注,但似乎没有达到预期的效果。 I've also tried replacing the XMLRootElement object (and XMLType, XMLElement) with local classes but nothing seems to be working properly as some information always seems to be retained somewhere even when it seems that I have changed the member/attribute/etc. 我也尝试过用本地类替换XMLRootElement对象(以及XMLType,XMLElement),但是似乎没有任何正常工作,因为即使我更改了member / attribute / etc,某些信息似乎总是保留在某个地方。

I am not going to duplicate all the java objects on a yearly basis just so that I can change the namespaces to match the requirements. 我不会每年重复所有Java对象,以便可以更改名称空间以符合要求。

Right now I'm at the point where I think XSLT might be the last option but I have little to no knowledge of it. 现在,我认为XSLT可能是最后的选择,但是我对此几乎一无所知。 Is there a simple way to update 5-8 namespace URI's that are located on the root element? 有没有一种简单的方法来更新位于根元素上的5-8名称空间URI? I don't want to change the prefixes (they are already set using a prefix mapper), I just want to change the namespace from "com.help.me.2014" to "com.help.me.2015". 我不想更改前缀(它们已经使用前缀映射器设置了),我只想将名称空间从“ com.help.me.2014”更改为“ com.help.me.2015”。

Thanks Andy 谢谢安迪

Resolution: 解析度:

First off I greatly appreciate the effort and responses. 首先,我非常感谢您的努力和回应。 I didn't actually try any of them as I came up with a different solution prior to getting back to see them. 我实际上并没有尝试过其中任何一个,因为在回来看它们之前,我想出了一个不同的解决方案。

Anyone coming along in the future can try the items listed below (as an XSLT solution) or you can try what I describe below. 将来出现的任何人都可以尝试下面列出的项目(作为XSLT解决方案),也可以尝试下面我描述的内容。

I am generating XML in two different styles/formats, one with and one without SOAP wrappers. 我正在以两种不同的样式/格式生成XML,一种具有/没有SOAP包装器。 Due to my difficulty accessing the actual namespaces within the DOM/SOAP objects and my inability to alter the annotations at runtime I ended up capturing the output stream and manipulating the resulting string. 由于我很难访问DOM / SOAP对象中的实际名称空间,并且由于无法在运行时更改批注,我最终捕获了输出流并处理了结果字符串。

SOAP: 肥皂:

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

... manipulate file (now a string), replace values, etc. -> actually passed to dependency injected converters, then send on to client via response.write ...操作文件(现在是字符串),替换值等。->实际上传递给依赖项注入的转换器,然后通过response.write发送给客户端

JAXB Marshalling is very similar to the SOAP, both send the resulting String onto converters which manipulate it as a StringBuilder then send it on. JAXB编组与SOAP非常相似,都将结果String发送到转换器上,后者将其作为StringBuilder进行操作,然后再将其发送。

Thanks again for the suggestions. 再次感谢您的建议。 Hopefully it helps someone in the future although the requirement is a little out there. 希望它对将来的人有所帮助,尽管要求不高。

Andy 安迪

Changing namespaces every year is almost certainly the wrong thing to do, but the following XSLT stylesheet will change namespaces 每年更改名称空间几乎肯定是错误的做法,但是以下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>

This style sheet creates a copy of every element changing the namespace from oldspace to newspace when appropriate. 此样式表将创建每个元素的副本, 在适当时将名称空间从oldspace更改newspace Other than the namespace change, the original document is preserved. 除了名称空间更改之外,还保留原始文档。 Similar templates can be made for every namespace that needs to be changed (note there are two templates that are namespace specific). 可以为每个需要更改的名称空间创建类似的模板(请注意,有两个模板是特定于名称空间的)。

Note that prefixes WILL be altered. 请注意,前缀将被更改。 These are not really content as such, so it is nearly impossible to preserve them in a case like this. 这些都不是真正满足的内容,因此在这种情况下几乎不可能保存它们。 The only way I can think of to preserve those would involve writing a separate template for each element in the original, directly creating the new elements instead of using the xsl:element element. 我能想到的唯一保留这些方法的方法是为原始文件中的每个元素编写一个单独的模板,直接创建新元素,而不是使用xsl:element元素。

For example, the given xml 例如,给定的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>

is transformed to 转化为

<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>

where all elements that were in the oldspace namespace are now in the newspace namespace. 现在空间名称空间中的所有元素现在都在空间名称空间中。

Here's an option that allows you to pass the old and new namespace URIs in as xsl:param s. 这是一个允许您将旧名称空间URI和新名称空间URI作为xsl:param传递的选项。

XML Input (Borrowed from Matthew's answer; thanks!) XML输入 (从马修的答案中借来;谢谢!)

<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 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 Output 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>

Here's an XSLT 2.0 option that produces the same output... 这是一个产生相同输出的XSLT 2.0选项...

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>

Here's another 2.0 example that handles multiple namespace uris. 这是另一个处理多个名称空间uri的2.0示例。 The old and new uris are passed in as a string with commas as delimiters. 旧的和新的uri以字符串形式传入,并以逗号作为分隔符。

The order of the uris are important. uri的顺序很重要。 The first old uri corresponds to the first new uri. 第一个旧uri对应于第一个新uri。 The second old uri corresponds to the second new uri. 第二旧uri对应于第二新uri。 Etc. 等等。

XML Input (updated to have more than one namespace 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 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 Output 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM