我开始使用XSLT暂时支持当前的1.0版web服务,而客户端转换为1.1,将旧调用转换为新格式。
对于此类更改,我需要更改命名空间,包含节点并重命名另一个节点。我是XSLT的新手,但经过一些谷歌搜索后,我想出了一个有效的解决方案,但是它的工作和输出看起来很混乱,而且我不确定它有多少故障保护。我想要一些建议来增强它,使其更清晰,更易于维护(版本1.2将需要更多的转换)。
输入XML示例(我的SOAP客户端使用限定元素):
<ns10:testRequest xmlns:ns10="namespace/1.0">
<ns10:a>
<ns10:b1>
<ns10:c>cccc</ns10:c>
<ns10:d>dddd</ns10:d>
<ns10:e>eeee</ns10:e>
</ns10:b1>
<ns10:b2 attr="value">
<ns10:f>false</ns10:f>
<ns10:g>2014-03-01</ns10:g>
<ns10:h>true</ns10:h>
</ns10:b2>
<ns10:b3>
</ns10:b3>
</ns10:a>
</ns10:testRequest>
XSLT(附有相关评论):
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oldns="namespace/1.0"
xmlns:newns="namespace/1.1"
version="1.0">
<xsl:param name="newnsParam">namespace/1.1</xsl:param>
<!-- copy all document -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!-- rename 'g' to 'newName' -->
<xsl:template match="/oldns:testRequest/oldns:a/oldns:b1/oldns:g">
<!-- if I don't set it with newns now, it will remain as oldns even after namespace change below -->
<xsl:element name="newName" namespace="{$newnsParam}">
<xsl:apply-templates select="@*|node()" />
</xsl:element>
</xsl:template>
<!-- add 'newElement' -->
<xsl:template match="/oldns:testRequest/oldns:a/oldns:b1">
<!-- same as above, must set as newns now, and this one won't be qualified (why?) -->
<xsl:element name="b1" namespace="{$newnsParam}">
<xsl:apply-templates select="@*|node()" />
<xsl:element name="newElement" namespace="{$newnsParam}">NEW_VAL</xsl:element>
</xsl:element>
</xsl:template>
<!-- change namespace, but it makes every node redefine the namespace -->
<xsl:template match="@oldns:*">
<xsl:attribute name="newns:{local-name()}" namespace="{$newnsParam}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="oldns:*">
<xsl:element name="newns:{local-name()}" namespace="{$newnsParam}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
输出XML:
<newns:testRequest xmlns:newns="namespace/1.1">
<newns:a xmlns:newns="namespace/1.1">
<newns:b1 xmlns:newns="namespace/1.1">
<newns:c xmlns:newns="namespace/1.1">cccc</newns:c>
<newns:d xmlns:newns="namespace/1.1">dddd</newns:d>
<newns:e xmlns:newns="namespace/1.1">eeee</newns:e>
</newns:b1>
<newns:b2 xmlns="namespace/1.1" attr="value" xmlns:newns="namespace/1.1">
<newns:f xmlns="namespace/1.1" xmlns:newns="namespace/1.1">false</newns:f>
<newns:newName xmlns="namespace/1.1" xmlns:newns="namespace/1.1">2014-03-01</newns:newName>
<newns:h xmlns="namespace/1.1" xmlns:newns="namespace/1.1">true</newns:h>
<newElement xmlns="namespace/1.1" xmlns:newns="namespace/1.1">NEW_VAL</newElement>
</newns:b2>
<newns:b3 xmlns:newns="namespace/1.1">
</newns:b3>
</newns:a>
</newns:testRequest>
如图所示,它与所有那些不必要的命名空间定义相当混淆,加上'newElement'是奇怪的不合格。我们记录调试以进行调试和审计,这种冗长是不可取的。
预期的XML(或类似的东西):
<newns:testRequest xmlns:newns="namespace/1.1">
<newns:a>
<newns:b1>
<newns:c>cccc</newns:c>
<newns:d>dddd</newns:d>
<newns:e>eeee</newns:e>
</newns:b1>
<newns:b2 attr="value">
<newns:f>false</newns:f>
<newns:newName>2014-03-01</newns:newName>
<newns:h>true</newns:h>
<newns:newElement>NEW_VAL</newns:newElement>
</newns:b2>
<newns:b3>
</newns:b3>
</newns:a>
</newns:testRequest>
重要的是,XSLT可以很好地处理不同的输入XML,例如合格和非限定元素(它接受当前的XSLT)。
感谢任何帮助。
环境:Java 1.6.0_14,带有Spring WS 2.1.0的Spring 3.1.3 Web应用程序,JAXB 2.2.6和内部JRE Apache Xalan
编辑1: hr_117的建议给出了这个例外:
javax.xml.transform.TransformerException: java.lang.RuntimeException: Namespace for prefix 'newns' has not been declared.
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:717) ~[na:1.6.0_14]
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:313) ~[na:1.6.0_14]
at org.springframework.ws.server.endpoint.interceptor.PayloadTransformingInterceptor.transformMessage(PayloadTransformingInterceptor.java:118) ~[spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.server.endpoint.interceptor.PayloadTransformingInterceptor.handleRequest(PayloadTransformingInterceptor.java:92) ~[spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.server.endpoint.interceptor.DelegatingSmartEndpointInterceptor.handleRequest(DelegatingSmartEndpointInterceptor.java:78) ~[spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.server.MessageDispatcher.dispatch(MessageDispatcher.java:224) [spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.server.MessageDispatcher.receive(MessageDispatcher.java:173) [spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:88) [spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:59) [spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:221) [spring-ws-core-2.1.0.RELEASE.jar:na]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882) [org.springframework.web.servlet-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789) [org.springframework.web.servlet-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) [javax.servlet_1.0.0.0_2-5.jar:2.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [javax.servlet_1.0.0.0_2-5.jar:2.5]
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227) [weblogic.jar:10.3.2.0]
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125) [weblogic.jar:10.3.2.0]
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292) [weblogic.jar:10.3.2.0]
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:175) [weblogic.jar:10.3.2.0]
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3591) [weblogic.jar:10.3.2.0]
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321) [com.bea.core.weblogic.security.identity_1.1.2.0.jar:1.1.2.0]
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121) [com.bea.core.weblogic.security.wls_1.0.0.0_5-2-0-0.jar:5.2.0.0]
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2202) [weblogic.jar:10.3.2.0]
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2108) [weblogic.jar:10.3.2.0]
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1432) [weblogic.jar:10.3.2.0]
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201) [com.bea.core.weblogic.workmanager_1.7.0.0.jar:1.7.0.0]
at weblogic.work.ExecuteThread.run(ExecuteThread.java:173) [com.bea.core.weblogic.workmanager_1.7.0.0.jar:1.7.0.0]
Caused by: java.lang.RuntimeException: Namespace for prefix 'newns' has not been declared.
at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.runTimeError(BasisLibrary.java:1518) ~[na:1.6.0_14]
at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.runTimeError(BasisLibrary.java:1522) ~[na:1.6.0_14]
at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.startXslElement(BasisLibrary.java:1408) ~[na:1.6.0_14]
at transformation_1_0_to_1_1.template$dot$4() ~[na:na]
at transformation_1_0_to_1_1.applyTemplates() ~[na:na]
at transformation_1_0_to_1_1.applyTemplates() ~[na:na]
at transformation_1_0_to_1_1.transform() ~[na:na]
at com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet.transform(AbstractTranslet.java:602) ~[na:1.6.0_14]
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:710) ~[na:1.6.0_14]
... 25 common frames omitted
EDIR 2:作为测试,我用Saxon取代了Xalan,输出很像迈克尔发布的。但是,对于我们已经很大的应用程序来说,这是一个很大的jar,加上它与我们应用程序的其他部分相冲突,所以它更像是一个不稳定因素。我想把重点放在Xalan上,让它正常工作。
答案 0 :(得分:2)
我很惊讶Xalan应该生成所有冗余的名称空间声明。以下是Saxon的比较输出:
<?xml version="1.0" encoding="UTF-8"?><newns:testRequest xmlns:newns="namespace/1.1">
<newns:a>
<b1 xmlns="namespace/1.1">
<newns:c>cccc</newns:c>
<newns:d>dddd</newns:d>
<newns:e>eeee</newns:e>
<newElement>NEW_VAL</newElement></b1>
<newns:b2 attr="value">
<newns:f>false</newns:f>
<newns:g>2014-03-01</newns:g>
<newns:h>true</newns:h>
</newns:b2>
<newns:b3>
</newns:b3>
</newns:a>
</newns:testRequest>
如果要使主导命名空间newns成为默认命名空间,为了进一步减少混乱,您可以将<xsl:copy>
更改为<xsl:element name="{local-name()}" namespace="{namespace-uri()}">
。这会产生输出:
<?xml version="1.0" encoding="UTF-8"?>
<testRequest xmlns="namespace/1.1">
<a>
<b1>
<c>cccc</c>
<d>dddd</d>
<e>eeee</e>
<newElement>NEW_VAL</newElement></b1>
<b2 attr="value">
<f>false</f>
<g>2014-03-01</g>
<h>true</h>
</b2>
<b3>
</b3>
</a>
</testRequest>
这是修改后的样式表:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oldns="namespace/1.0"
xmlns:newns="namespace/1.1"
version="1.0">
<xsl:param name="newnsParam">namespace/1.1</xsl:param>
<!-- copy all document -->
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="*" />
</xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:copy-of select="."/>
</xsl:template>
<!-- rename 'g' to 'newName' -->
<xsl:template match="/oldns:testRequest/oldns:a/oldns:b1/oldns:g">
<!-- if I don't set it with newns now, it will remain as oldns even after namespace change below -->
<xsl:element name="newName" namespace="{$newnsParam}">
<xsl:apply-templates select="@*|node()" />
</xsl:element>
</xsl:template>
<!-- add 'newElement' -->
<xsl:template match="/oldns:testRequest/oldns:a/oldns:b1">
<!-- same as above, must set as newns now, and this one won't be qualified (why?) -->
<xsl:element name="b1" namespace="{$newnsParam}">
<xsl:apply-templates select="@*|node()" />
<xsl:element name="newElement" namespace="{$newnsParam}">NEW_VAL</xsl:element>
</xsl:element>
</xsl:template>
<!-- change namespace, but it makes every node redefine the namespace -->
<xsl:template match="@oldns:*">
<xsl:attribute name="newns:{local-name()}" namespace="{$newnsParam}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="oldns:*">
<xsl:element name="{local-name()}" namespace="{$newnsParam}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
我不确定这是否适合您,因为我使用的是不同的xlst处理器(xsltproc)。 我的原始xslt的输出也不完全像你的输出。
无论如何:
我主要从xlst中删除了namespace属性,并添加了两次命名空间前缀“newns”。
试试这个:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oldns="namespace/1.0"
xmlns:newns="namespace/1.1"
version="1.0">
<xsl:param name="newnsParam">namespace/1.1</xsl:param>
<!-- copy all document -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!-- rename 'g' to 'newName' -->
<xsl:template match="/oldns:testRequest/oldns:a/oldns:b1/oldns:g">
<!-- if I don't set it with newns now, it will remain as oldns even after namespace change below -->
<xsl:element name="newns:newName">
<xsl:apply-templates select="@*|node()" />
</xsl:element>
</xsl:template>
<!-- add 'newElement' -->
<xsl:template match="/oldns:testRequest/oldns:a/oldns:b1">
<!-- same as above, must set as newns now, and this one won't be qualified (why?) -->
<xsl:element name="newns:b1" >
<xsl:apply-templates select="@*|node()" />
<xsl:element name="newns:newElement">NEW_VAL</xsl:element>
</xsl:element>
</xsl:template>
<!-- change namespace, but it makes every node redefine the namespace -->
<xsl:template match="@oldns:*">
<xsl:attribute name="newns:{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="oldns:*">
<xsl:element name="newns:{local-name()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
生成以下输出(至少使用xsltproc):
<?xml version="1.0"?>
<newns:testRequest xmlns:newns="namespace/1.1">
<newns:a>
<newns:b1>
<newns:c>cccc</newns:c>
<newns:d>dddd</newns:d>
<newns:e>eeee</newns:e>
<newns:newElement>NEW_VAL</newns:newElement></newns:b1>
<newns:b2 attr="value">
<newns:f>false</newns:f>
<newns:g>2014-03-01</newns:g>
<newns:h>true</newns:h>
</newns:b2>
<newns:b3>
</newns:b3>
</newns:a>
</newns:testRequest>