XSLT 2.0灵活,具有相同URI的默认或不同的前缀命名空间

时间:2016-08-17 13:15:31

标签: xslt-2.0 xml-namespaces

我是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$1.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$2.doInProducer(ProducerCache.java:412)
    at org.apache.camel.impl.ProducerCache$2.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

即使这些xmls在语义上是等效的,只要涉及xml规范,XSLT转换器就会被挂起,因为一个声明了前缀而另一个没有声明(我也想冒险说出来)如果一个人以“foo”为前缀,而另一个人以&#39; bar&#39;)为前提,我们会挂断电话。

我处在一个我无法强迫客户传递给我xml以某种方式声明特定前缀或命名空间的位置。我也不能保证他们明天不会决定使用不同的前缀别名。

我对声明xpath-default-namespace属性的理解是它告诉xslt转换器整个文档将与哪个命名空间URI相关联,而不管它是否会被声明为默认前缀,带有别名的前缀&# 39;膨胀&#39;,甚至是带别名的前缀&#39; rainbowunicorns&#39;。

属性xpath-default-namespace究竟做了什么以及如何编写一个灵活的XSLT,它可以优雅地处理任意数量的语义等效命名空间,而不管客户端决定什么样的命名空间声明?

规格如果相关: 骆驼2.16.2 Saxon-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>

2 个答案:

答案 0 :(得分:2)

替换

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

答案 1 :(得分:1)

首先谈谈诊断​​:当样式表中出现动态错误时,打印Java堆栈跟踪没有多大意义。 Saxon将错误报告给已注册的ErrorListener,标准的ErrorListener生成诊断程序,这些诊断程序对样式表作者有用,告诉您XSLT中发生故障的位置,以及显示从哪里调用代码的XSLT级堆栈跟踪。如果您没有看到这些诊断信息,那么您就会做错事。

第二,为什么会失败?你在做什么

<xsl:element name="{name()}">

并且name()的值类似于bloat:RqUID。 xsl:element的规则说:

  

[ERR XTDE0830]对于没有的xsl:element指令   如果是namespace属性,那么它是一个不可恢复的动态错误   name属性的有效值是前缀不是的QName   在xsl:元素的范围内命名空间声明中声明   指令。

如果您要更改名称,请使用<xsl:copy>,或者如果您愿意,请<xsl:element name="{name()}" namespace="{namespace-uri()}">