将命名空间添加到根元素

时间:2017-06-23 13:42:57

标签: c# .net xslt xslcompiledtransform

我正在编写一个XSLT转换,我希望在根元素上定义所有名称空间前缀。默认情况下,MS似乎在XML层次结构的第一个元素上创建一个新的前缀定义来使用该模式;意味着如果这些元素与同一模式的共享祖先无关,则可以在多个元素上引用相同的模式。

通过对根元素进行编码,所有工作都可以正常工作:

<!-- ... -->

<ns0:root xmlns:ns0="http://some/schema" xmlns:ns1 = "http://another/schema">
    <!-- rest of XSLT; including calls to other templates -->
</ns0:root>

<!-- ... -->

但是我找不到使用xsl:element对此进行编码的方法; e.g。

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns0="http://some/schema" 
    xmlns:ns1 = "http://another/schema"
>
    <!-- ... -->

    <xsl:element name="ns0:root">
        <xsl:attribute name="ns1" namespace="http://www.w3.org/2000/xslns/">http://another/schema</xsl:attribute>
        <!-- rest of XSLT; including calls to other templates -->
    </xsl:element> 

    <!-- ... -->

对于除该元素本身以外的模式,是否可以针对xls:element声明名称空间前缀?

完整示例

XML

<Demo xmlns="http://some/schema">
    <a>Hello</a>
    <b>World</b>
</Demo>

XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns0="http://some/schema" 
    xmlns:ns1 = "http://another/schema"
    exclude-result-prefixes="xsl"
>

    <xsl:output method="xml" indent="yes" version="1.0"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/*"> 
        <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
            <xsl:apply-templates select="@* | node()" />
        </xsl:element>
    </xsl:template> 

    <xsl:template match="/ns0:Demo/ns0:a">
        <xsl:element name="ns1:z">
            <xsl:value-of select="./text()" />
        </xsl:element>
    </xsl:template> 

    <xsl:template match="/ns0:Demo/ns0:b">
        <xsl:element name="ns1:y">
            <xsl:value-of select="./text()" />
        </xsl:element>
    </xsl:template> 

</xsl:stylesheet>

结果

<Demo xmlns="http://some/schema">
    <ns1:z xmlns:ns1="http://another/schema">Hello</ns1:z>
    <ns1:y xmlns:ns1="http://another/schema">World</ns1:y>
</Demo>

期望的结果

<Demo xmlns="http://some/schema" xmlns:ns1="http://another/schema">
    <ns1:z>Hello</ns1:z>
    <ns1:y>World</ns1:y>
</Demo>

<ns0:Demo xmlns:ns0="http://some/schema" xmlns:ns1="http://another/schema">
    <ns1:z>Hello</ns1:z>
    <ns1:y>World</ns1:y>
</ns0:Demo>

2 个答案:

答案 0 :(得分:2)

您的最小示例没有解释为什么您需要使用xsl:element而不是xsl:copy和/或文字结果元素,但因为XSLT 1.0没有xsl:namespace指令({{3}您唯一的方法是从样式表根目录复制命名空间节点,如

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns0="http://some/schema" 
    xmlns:ns1 = "http://another/schema"
    exclude-result-prefixes="xsl"
    >

    <xsl:output method="xml" indent="yes" version="1.0"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/*"> 
        <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
            <xsl:copy-of select="document('')/*/namespace::*[. = 'http://another/schema']"/>
            <xsl:apply-templates select="@* | node()" />
        </xsl:element>
    </xsl:template> 

    <xsl:template match="/ns0:Demo/ns0:a">
        <xsl:element name="ns1:z">
            <xsl:value-of select="./text()" />
        </xsl:element>
    </xsl:template> 

    <xsl:template match="/ns0:Demo/ns0:b">
        <xsl:element name="ns1:y">
            <xsl:value-of select="./text()" />
        </xsl:element>
    </xsl:template> 

</xsl:stylesheet>

(或具有该参数或变量的任何其他节点,但这样您还可以将结果树片段转换为首先使用exsl:node-setms:node-set设置的节点。

至于为什么文字结果元素和xsl:element会给你不同的结果,https://www.w3.org/TR/xslt20/#creating-namespace-nodes说:

  

创建的元素节点还将具有命名空间节点的副本   在样式表树中的元素节点上出现...

虽然https://www.w3.org/TR/xslt#literal-result-element没有这么说。

答案 1 :(得分:1)

重要的是要理解虽然它们通过名称空间声明属性在XML文档中表示,但在XPath和XSLT的数据模型中,每个元素的范围内命名空间都是通过命名空间节点而不是属性节点建模的。而且,不同的元素不共享命名空间节点;每个人都有自己的一套。使用XML输出方法时,XSLT处理器负责生成名称空间声明属性,这些属性正确表示结果树中存在的名称空间节点。

这完全解释了为什么Section 7.1.3 of the XSLT 1.0 spec明确禁止通过var filePath = await DependencyService.Get<IMediaPicker>().ChooseAFileAsync(); System.Diagnostics.Debug.WriteLine(filePath); 元素创建名称空间声明:

  

XSLT处理器可以使用QName的前缀   选择用于的前缀时,在name属性中指定   将创建的属性输出为XML;但是,他们不是   要求这样做,如果前缀是xsl:attribute,则不得这样做。   因此,虽然这不是一个错误:

xmlns
     

它不会导致输出名称空间声明。

(强调补充。)如果以允许的方式创建名称空间声明,那么它将允许结果文档表示实际上不存在于结果树中的名称空间节点。

结果树中的元素可以通过以下任何方式获取命名空间节点:

  • 通过<xsl:attribute name="xmlns:xsl" namespace="whatever">http://www.w3.org/1999/XSL/Transform</xsl:attribute> xsl:copy创建的结果元素接收原始元素命名空间节点的副本。
  • 通过样式表树中的文字结果元素创建的结果元素获取样式表元素的所有命名空间节点的副本,无论是直接在该元素上还是在祖先元素上声明,都有一些例外。
  • 通过xsl:copy-of样式表元素创建的结果元素未明确指定接收任何命名空间节点,但实际上,要正确实现接收命名空间的命名空间节点所需的规范(如果有的话)元素的名称。
  • 因为只有元素具有命名空间节点,所以它遵循(但未明确指定)每个元素还必须为其属性名称所属的每个命名空间接收命名空间节点,如果该命名空间与元素的命名空间不同自己的名字。
  • 命名空间节点本身可以​​复制到结果树,如另一个答案所示。

没有理由期望XSLT处理器在结果树中创建额外的命名空间节点。特别是,虽然这可能提供结果树更简单的XML序列化的可能性,但树本身将严格地更复杂。

然后,一种方法是确保结果文档中的xsl:element元素包含除该元素之外的命名空间的命名空间声明,通过从输入树复制结果元素获得的任何元数据,或者元素的一个属性,是使用文字结果元素:

<Demo>

另一方面,如果必须通过<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://some/schema" xmlns:ns1 = "http://another/schema"> <xsl:output method="xml" indent="yes" version="1.0"/> <xsl:strip-space elements="*"/> <xsl:template match="/ns0:Demo"> <ns0:Demo> <xsl:apply-templates select="@* | node()" /> <ns0:Demo> </xsl:template> <!-- ... --> </xsl:stylesheet> 元素创建元素 - 只有在需要计算其名称时才需要 - 那么您需要从中复制命名空间节点输入树。