XSLT 2.0 灵活,具有相同 URI 的默认或不同前缀命名空间
XSLT 2.0 flexible with default or different prefixed namespace of the same URI
我是 XSLT 的新手,正在处理一些样板名称空间处理。
我有以下 xslt,其目标是简单地重命名一个元素:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" >
<xsl:strip-space elements="*" />
<!-- element template that copies over elements -->
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- attribute template to copy attributes over -->
<xsl:template match="@*">
<xsl:copy>
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:copy>
</xsl:template>
<!-- "other" template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<!-- Rename an element -->
<xsl:template match="BOPPolicyQuoteInqRq/RqUID" >
<xsl:element name="RqUUID">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
转换以下 xml 按预期工作:
<ACORD xmlns="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/">
<InsuranceSvcRq>
<BOPPolicyQuoteInqRq>
<RqUID>E2BA6308-62D5-43AC-B8C1-7616FDFE9C98</RqUID>
</BOPPolicyQuoteInqRq>
</InsuranceSvcRq>
</ACORD>
然而,这个语义等价的 xml 失败了:
<bloat:ACORD xmlns:bloat="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/">
<bloat:InsuranceSvcRq>
<bloat:BOPPolicyQuoteInqRq>
<bloat:RqUID>E2BA6308-62D5-43AC-B8C1-7616FDFE9C98</bloat:RqUID>
</bloat:BOPPolicyQuoteInqRq>
</bloat:InsuranceSvcRq>
</bloat:ACORD>
我收到的错误是:
Caused by: net.sf.saxon.trans.XPathException: Undeclared prefix in element name: bloat
at net.sf.saxon.expr.instruct.ComputedElement.getElementName(ComputedElement.java:429)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:388)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:371)
at net.sf.saxon.expr.instruct.Template.applyLeavingTail(Template.java:239)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:1056)
at net.sf.saxon.trans.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:65)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:1044)
at net.sf.saxon.Controller.transformDocument(Controller.java:2088)
at net.sf.saxon.Controller.transform(Controller.java:1911)
at org.apache.camel.builder.xml.XsltBuilder.process(XsltBuilder.java:141)
at org.apache.camel.impl.ProcessorEndpoint.onExchange(ProcessorEndpoint.java:103)
at org.apache.camel.component.xslt.XsltEndpoint.onExchange(XsltEndpoint.java:121)
at org.apache.camel.impl.ProcessorEndpoint.process(ProcessorEndpoint.java:71)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:141)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:460)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:121)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:62)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:109)
at org.apache.camel.processor.UnitOfWorkProducer.process(UnitOfWorkProducer.java:68)
at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:412)
at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:380)
at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:270)
at org.apache.camel.impl.ProducerCache.sendExchange(ProducerCache.java:380)
at org.apache.camel.impl.ProducerCache.send(ProducerCache.java:221)
at org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:124)
at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:137)
... 32 more
尽管这些 xml 就 xml 规范而言在语义上是等价的,但 XSLT 转换器正在挂起,因为一个声明了前缀而另一个没有声明前缀' t(我也冒昧地说,如果一个前缀为 'foo',另一个前缀为 'bar',它将被挂断)。
我无法强迫经过我的客户 xml 以某种方式声明特定的前缀或名称空间。我也不能保证他们明天不会决定使用不同的前缀别名。
我对声明 xpath-default-namespace
属性的理解是,它告诉 xslt 转换器整个文档将与哪个命名空间 URI 相关联,而不管它是否被声明为默认前缀,带有别名的前缀'bloat',甚至是带有别名 'rainbowunicorns'.
的前缀
属性 xpath-default-namespace
的确切作用是什么?我如何才能编写一个灵活的 XSLT,无论客户决定使用哪种类型的名称空间声明,它都可以优雅地处理任意数量的语义等效名称空间?
相关规格:
骆驼 2.16.2
撒克逊-HE 9.5.1-8
更新后的转换适用于 xmls(由 Martin Honnen 提供):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" >
<xsl:strip-space elements="*" />
<!-- element template that copies over elements -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<!-- "other" template to copy the rest of the nodes -->
<xsl:template match="comment() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<!-- Rename an element -->
<xsl:template match="BOPPolicyQuoteInqRq/RqUID" >
<xsl:element name="RqUUID" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
替换
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- attribute template to copy attributes over -->
<xsl:template match="@*">
<xsl:copy>
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:copy>
</xsl:template>
来自
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
这样你也可以复制范围内的任何命名空间,然后使用
<xsl:template match="BOPPolicyQuoteInqRq/RqUID" >
<xsl:element name="{QName(namespace-uri(), if (prefix-from-QName(node-name(.))) then concat(prefix-from-QName(node-name(.)),':', 'RqUUID') else 'RqUUID')}" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
使用一些变量来保持代码的可读性,整个样式表就变成了
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="BOPPolicyQuoteInqRq/RqUID">
<xsl:variable name="new-local-name" as="xs:string" select="'RqUUID'"/>
<xsl:variable name="prefix" select="prefix-from-QName(node-name(.))"/>
<xsl:variable name="new-name" as="xs:string" select="if ($prefix) then concat($prefix,':', $new-local-name) else $new-local-name"/>
<xsl:element name="{QName(namespace-uri(), $new-name)}" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:transform>
首先谈谈诊断:当您在样式表中遇到动态错误时,打印 Java 堆栈跟踪没有多大意义。 Saxon 向已注册的 ErrorListener 报告错误,标准 ErrorListener 生成旨在对样式表作者有用的诊断,告诉您 XSLT 中发生故障的位置,以及 XSLT 级别的堆栈跟踪,显示从何处调用该代码.如果您没有看到这些诊断信息,那么您做错了什么。
第二,为什么会失败?你在做
<xsl:element name="{name()}">
name() 的值类似于 bloat:RqUID
。 xsl:element 的规则是:
[ERR XTDE0830] In the case of an xsl:element instruction with no
namespace attribute, it is a non-recoverable dynamic error if the
effective value of the name attribute is a QName whose prefix is not
declared in an in-scope namespace declaration for the xsl:element
instruction.
如果您想原封不动地复制名称,请使用 <xsl:copy>
,或者如果您愿意,可以使用 <xsl:element name="{name()}" namespace="{namespace-uri()}">
我是 XSLT 的新手,正在处理一些样板名称空间处理。
我有以下 xslt,其目标是简单地重命名一个元素:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" >
<xsl:strip-space elements="*" />
<!-- element template that copies over elements -->
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- attribute template to copy attributes over -->
<xsl:template match="@*">
<xsl:copy>
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:copy>
</xsl:template>
<!-- "other" template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<!-- Rename an element -->
<xsl:template match="BOPPolicyQuoteInqRq/RqUID" >
<xsl:element name="RqUUID">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
转换以下 xml 按预期工作:
<ACORD xmlns="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/">
<InsuranceSvcRq>
<BOPPolicyQuoteInqRq>
<RqUID>E2BA6308-62D5-43AC-B8C1-7616FDFE9C98</RqUID>
</BOPPolicyQuoteInqRq>
</InsuranceSvcRq>
</ACORD>
然而,这个语义等价的 xml 失败了:
<bloat:ACORD xmlns:bloat="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/">
<bloat:InsuranceSvcRq>
<bloat:BOPPolicyQuoteInqRq>
<bloat:RqUID>E2BA6308-62D5-43AC-B8C1-7616FDFE9C98</bloat:RqUID>
</bloat:BOPPolicyQuoteInqRq>
</bloat:InsuranceSvcRq>
</bloat:ACORD>
我收到的错误是:
Caused by: net.sf.saxon.trans.XPathException: Undeclared prefix in element name: bloat
at net.sf.saxon.expr.instruct.ComputedElement.getElementName(ComputedElement.java:429)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:388)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:371)
at net.sf.saxon.expr.instruct.Template.applyLeavingTail(Template.java:239)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:1056)
at net.sf.saxon.trans.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:65)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:1044)
at net.sf.saxon.Controller.transformDocument(Controller.java:2088)
at net.sf.saxon.Controller.transform(Controller.java:1911)
at org.apache.camel.builder.xml.XsltBuilder.process(XsltBuilder.java:141)
at org.apache.camel.impl.ProcessorEndpoint.onExchange(ProcessorEndpoint.java:103)
at org.apache.camel.component.xslt.XsltEndpoint.onExchange(XsltEndpoint.java:121)
at org.apache.camel.impl.ProcessorEndpoint.process(ProcessorEndpoint.java:71)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:141)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:460)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:121)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:62)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:109)
at org.apache.camel.processor.UnitOfWorkProducer.process(UnitOfWorkProducer.java:68)
at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:412)
at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:380)
at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:270)
at org.apache.camel.impl.ProducerCache.sendExchange(ProducerCache.java:380)
at org.apache.camel.impl.ProducerCache.send(ProducerCache.java:221)
at org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:124)
at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:137)
... 32 more
尽管这些 xml 就 xml 规范而言在语义上是等价的,但 XSLT 转换器正在挂起,因为一个声明了前缀而另一个没有声明前缀' t(我也冒昧地说,如果一个前缀为 'foo',另一个前缀为 'bar',它将被挂断)。
我无法强迫经过我的客户 xml 以某种方式声明特定的前缀或名称空间。我也不能保证他们明天不会决定使用不同的前缀别名。
我对声明 xpath-default-namespace
属性的理解是,它告诉 xslt 转换器整个文档将与哪个命名空间 URI 相关联,而不管它是否被声明为默认前缀,带有别名的前缀'bloat',甚至是带有别名 'rainbowunicorns'.
属性 xpath-default-namespace
的确切作用是什么?我如何才能编写一个灵活的 XSLT,无论客户决定使用哪种类型的名称空间声明,它都可以优雅地处理任意数量的语义等效名称空间?
相关规格: 骆驼 2.16.2 撒克逊-HE 9.5.1-8
更新后的转换适用于 xmls(由 Martin Honnen 提供):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" >
<xsl:strip-space elements="*" />
<!-- element template that copies over elements -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<!-- "other" template to copy the rest of the nodes -->
<xsl:template match="comment() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<!-- Rename an element -->
<xsl:template match="BOPPolicyQuoteInqRq/RqUID" >
<xsl:element name="RqUUID" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
替换
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- attribute template to copy attributes over -->
<xsl:template match="@*">
<xsl:copy>
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:copy>
</xsl:template>
来自
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
这样你也可以复制范围内的任何命名空间,然后使用
<xsl:template match="BOPPolicyQuoteInqRq/RqUID" >
<xsl:element name="{QName(namespace-uri(), if (prefix-from-QName(node-name(.))) then concat(prefix-from-QName(node-name(.)),':', 'RqUUID') else 'RqUUID')}" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
使用一些变量来保持代码的可读性,整个样式表就变成了
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="BOPPolicyQuoteInqRq/RqUID">
<xsl:variable name="new-local-name" as="xs:string" select="'RqUUID'"/>
<xsl:variable name="prefix" select="prefix-from-QName(node-name(.))"/>
<xsl:variable name="new-name" as="xs:string" select="if ($prefix) then concat($prefix,':', $new-local-name) else $new-local-name"/>
<xsl:element name="{QName(namespace-uri(), $new-name)}" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:transform>
首先谈谈诊断:当您在样式表中遇到动态错误时,打印 Java 堆栈跟踪没有多大意义。 Saxon 向已注册的 ErrorListener 报告错误,标准 ErrorListener 生成旨在对样式表作者有用的诊断,告诉您 XSLT 中发生故障的位置,以及 XSLT 级别的堆栈跟踪,显示从何处调用该代码.如果您没有看到这些诊断信息,那么您做错了什么。
第二,为什么会失败?你在做
<xsl:element name="{name()}">
name() 的值类似于 bloat:RqUID
。 xsl:element 的规则是:
[ERR XTDE0830] In the case of an xsl:element instruction with no namespace attribute, it is a non-recoverable dynamic error if the effective value of the name attribute is a QName whose prefix is not declared in an in-scope namespace declaration for the xsl:element instruction.
如果您想原封不动地复制名称,请使用 <xsl:copy>
,或者如果您愿意,可以使用 <xsl:element name="{name()}" namespace="{namespace-uri()}">