2016-03-14 48 views
0

我有许多Java类与JAXB结合使用以生成XML。 java类每年的变化很小,但输出的XML需要对其进行非常具体的年度更改,这样做有点难以捉摸。我试过使用DOM更新属性,但沿着树的更多节点保持以前的状态。我试着用反射来在编组之前直接更新注解,但它似乎没有达到预期的效果。我也尝试用本地类替换XMLRootElement对象(和XMLType,XMLElement),但似乎没有任何工作正常,因为某些信息似乎总是保留在某个地方,即使看起来我已更改成员/属性/等。在运行时动态设置XML URI - XSLT选项

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

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

感谢 安迪

分辨率:

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

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

我以两种不同的样式/格式生成XML,一种带有和没有SOAP包装。由于我在访问DOM/SOAP对象中的实际名称空间时遇到困难,并且无法在运行时更改注释,所以我最终捕获了输出流并操作了结果字符串。

SOAP:

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

...操作文件(现在是字符串),替换值等 - >实际传递进行依赖注入的转换器,然后通过发送响应到客户端。写

JAXB编组与SOAP非常相似,都将结果字符串发送到转换器,并将其作为StringBuilder操作,然后将其发送。

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

安迪

+0

这问题太广泛了,但是对于一个设计来说,类的变化很小,但每年都会改变5-8个名称空间。在你面临的任何其他挑战中,你可能会遭受糟糕设计决定的后果。 – kjhughes

+0

这可能很简单,只需在根上重写URI即可。前缀只是URI处的指针。不需要额外的映射。 –

+0

正如@kjhughes建议的那样,它看起来像在这里不恰当地使用命名空间。使用像_com.help.me_这样的固定名称空间和像'year ='2014''这样的属性似乎更合适。我相信这可以用XSLT来完成,但是操作这样的命名空间是非常棘手的,因为它们实际上并不是为这种目的而设计的。然而,我现在唯一能想到的唯一事情是在所有旧元素的新命名空间中创建生成的元素。尽管如此,这将改变前缀。 – Matthew

回答

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> 

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

请注意,前缀将被改变。这些都不是真正的内容,所以在这样的情况下保护它们几乎是不可能的。我能想到保留这些元素的唯一方法是为原始元素中的每个元素编写一个单独的模板,直接创建新元素而不是使用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命名空间的所有元素现在在时讯命名空间。

0

下面是一个选项,允许您传递旧的和新的命名空间URI作为xsl:param s。

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

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

这里还有一个2.0的例子,处理多个命名空间的URI。旧的和新的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>