【问题标题】:Cannot add namespace prefix to children using XSL无法使用 XSL 向子项添加命名空间前缀
【发布时间】:2011-02-27 22:20:00
【问题描述】:

我在这里检查了很多答案,我想我快到了。困扰我的一件事(出于某种原因,我的同行需要它)如下:

我有以下输入 XML:

<?xml version="1.0" encoding="utf-8"?>
<MyRoot>
  <MyRequest CompletionCode="0" CustomerID="9999999999"/>
  <List TotalList="1">
    <Order CustomerID="999999999" OrderNo="0000000001" Status="Shipped">
      <BillToAddress ZipCode="22221"/>
      <ShipToAddress ZipCode="22222"/>
      <Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/>
    </Order>
  </List>
  <Errors/>
</MyRoot>

我被要求制作这个:

<ns:MyNewRoot xmlns:ns="http://schemas.foo.com/response"  
xmlns:N1="http://schemas.foo.com/request"  
xmlns:N2="http://schemas.foo.com/details">
    <N1:MyRequest CompletionCode="0" CustomerID="9999999999"/>
    <ns:List TotalList="1">
            <N2:Order CustomerID="999999999" Level="Preferred" Status="Shipped">
                    <N2:BillToAddress ZipCode="22221"/>
                    <N2:ShipToAddress ZipCode="22222"/>
                    <N2:Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/>
            </N2:Order>
    </ns:List>
    <ns:Errors/>
</ns:MyNewRoot>

注意 N2:Order 的子元素还需要 N2: 前缀以及 ns: 其余元素的前缀。

我使用下面的 XSL 转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="@* | node()">
  <xsl:copy>
   <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
 </xsl:template>


<xsl:template match="/MyRoot">
 <MyNewRoot xmlns="http://schemas.foo.com/response"
   xmlns:N1="http://schemas.foo.com/request"
   xmlns:N2="http://schemas.foo.com/details">
     <xsl:apply-templates/>
 </MyNewRoot>
 </xsl:template>

<xsl:template match="/MyRoot/MyRequest">
  <xsl:element name="N1:{name()}" namespace="http://schemas.foo.com/request">
    <xsl:copy-of select="namespace::*"/>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="/MyRoot/List/Order">
  <xsl:element name="N2:{name()}" namespace="http://schemas.foo.com/details">
    <xsl:copy-of select="namespace::*"/>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
 </xsl:template>

</xsl:stylesheet>

这个不处理 ns(我想不通)。当我通过上述使用 AltovaXML 的 XSL 转换进行处理时,我最终得到以下结果:

<MyNewRoot xmlns="http://schemas.foo.com/response"  
xmlns:N1="http://schemas.foo.com/request"  
xmlns:N2="http://schemas.foo.com/details">
    <N1:MyRequest CompletionCode="0" CustomerID="9999999999"/>
    <List xmlns="" TotalList="1">
            <N2:Order CustomerID="999999999" Level="Preferred" Status="Shipped">
                    <BillToAddress ZipCode="22221"/>
                    <ShipToAddress ZipCode="22222"/>
                    <Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/>
            </N2:Order>
    </List>
    <Errors/>
</MyNewRoot>

请注意,在 XSL 转换之后,Order 子级的 N2: 前缀不存在。 Order 标头中还有额外的 xmlns="" (出于某种原因)。我想不出为其余元素(如错误和列表)添加 ns: 前缀。

首先,如果父母已经拥有前缀,为什么我需要为孩子添加前缀。父命名空间是否规定了子节点/属性命名空间?

其次,我想按预期在上面的 XML 中添加前缀,我该如何使用 XSL 呢?

【问题讨论】:

  • 欢迎来到 SO。第一个问题好!我建议您花时间阅读FAQ。您会看到这是一个问答网站,与论坛完全不同。这就是为什么我从您的问题中删除了“你好”和“谢谢” - 我们不在这里讨论!
  • 好问题 (+1)。请参阅我的答案以获得迄今为止最短/最简单和唯一正确的解决方案。 :)
  • 感谢大家的宝贵回答和见解。现在我对 XSL 的所有事情有了更好的理解......

标签: xml xslt namespaces transformation xml-namespaces


【解决方案1】:

如果您真的关心输出中的命名空间前缀,那么您将希望在模板中使用文字结果元素,而不是 xsl:element 构造函数:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns="http://schemas.foo.com/response"
    xmlns:N1="http://schemas.foo.com/request"  
    xmlns:N2="http://schemas.foo.com/details">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="MyRoot">
        <ns:MyNewRoot>
            <xsl:apply-templates/>
        </ns:MyNewRoot>
    </xsl:template>

    <xsl:template match="MyRequest">
        <N1:MyRequest>
            <xsl:apply-templates select="@* | node()"/>
        </N1:MyRequest>
    </xsl:template>

    <xsl:template match="List">
        <ns:List>
            <xsl:apply-templates select="@* | node()"/>
        </ns:List>
    </xsl:template>    

    <xsl:template match="Order">
        <N2:Order>
            <xsl:apply-templates select="@* | node()"/>
        </N2:Order>
    </xsl:template>

    <xsl:template match="BillToAddress">
        <N2:BillToAddress>
            <xsl:apply-templates select="@* | node()"/>
        </N2:BillToAddress>
    </xsl:template>

    <xsl:template match="ShipToAddress">
        <N2:ShipToAddress>
            <xsl:apply-templates select="@* | node()"/>
        </N2:ShipToAddress>
    </xsl:template>

    <xsl:template match="Totals">
        <N2:Totals>
            <xsl:apply-templates select="@* | node()"/>
        </N2:Totals>
    </xsl:template>

    <xsl:template match="Errors" />

</xsl:stylesheet>

你应该知道命名空间很重要,命名空间前缀。它是语法糖。您可以将多个命名空间前缀绑定到同一个命名空间uri,或者没有命名空间前缀但仍然产生相同类型的元素(绑定到特定的命名空间uri)。

【讨论】:

  • +1 表示前缀无关紧要。我更进一步:任何需要特定前缀的代码都被严重破坏,需要修复和/或公开嘲笑不遵循基本的 XML 标准。
  • 不幸的是,这种转换不会产生想要的输出——MyRequest 元素不在所需的命名空间中。还有就是太长了。
  • 哎呀,更正了 MyRequest 的命名空间。谢谢,@Dimitre。 - 出于某种原因,我想我记得 Ken Holman 警告说,在 xsl:element 声明的 @name 中指定命名空间前缀不能保证有效,这就是我使用文字元素声明的原因(以及为什么它这么长)。如果不是这种情况,那么您的解决方案肯定是首选。
  • @Mads-Hansen:如果您指定前缀,XSLT 处理器将使用它,即使需要在结果文档中重新定义命名空间绑定。
  • 这很棒。感谢你,我现在对我应该做的事情有了更好的理解,而不是仅仅进行一次转型。谢谢 Mads/John/Dimitre
【解决方案2】:

这个转换(只有 42 行)

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ns="http://schemas.foo.com/response"
 xmlns:N1="http://schemas.foo.com/request"
 xmlns:N2="http://schemas.foo.com/details"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="*">
  <xsl:element name="ns:{name()}"
       namespace="http://schemas.foo.com/response">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="@*">
   <xsl:copy-of select="."/>
 </xsl:template>

 <xsl:template match="/MyRoot">
  <xsl:element name="ns:{name()}"
       namespace="http://schemas.foo.com/response">
    <xsl:copy-of select=
     "document('')/*/namespace::*[name()='N1' or name()='N2']"/>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="MyRequest">
  <xsl:element name="N1:{name()}"
       namespace="http://schemas.foo.com/request">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="*[ancestor-or-self::Order]">
  <xsl:element name="N2:{name()}"
       namespace="http://schemas.foo.com/details">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<MyRoot>
  <MyRequest CompletionCode="0" CustomerID="9999999999"/>
  <List TotalList="1">
    <Order CustomerID="999999999" OrderNo="0000000001" Status="Shipped">
      <BillToAddress ZipCode="22221"/>
      <ShipToAddress ZipCode="22222"/>
      <Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/>
    </Order>
  </List>
  <Errors/>
</MyRoot>

产生想要的结果

<ns:MyRoot xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details" xmlns:ns="http://schemas.foo.com/response">
    <N1:MyRequest CompletionCode="0" CustomerID="9999999999"/>
    <ns:List TotalList="1">
        <N2:Order CustomerID="999999999" OrderNo="0000000001" Status="Shipped">
            <N2:BillToAddress ZipCode="22221"/>
            <N2:ShipToAddress ZipCode="22222"/>
            <N2:Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/>
        </N2:Order>
    </ns:List>
    <ns:Errors/>
</ns:MyRoot>

请注意

  1. &lt;xsl:element&gt; 及其namenamespace 属性的使用。

  2. identity template 是如何演变为转换的前两个模板的——这一决定是基于这样一个事实,即只有在特殊情况下,元素不得位于 ns: 命名空间中。

  3. 如何为 Order 元素或其任何后代元素指定 N2: 命名空间。

【讨论】:

  • +1 更简洁的答案。虽然,如果我理解 XSLT 规范的措辞,xsl:element@name 中指定的命名空间前缀 不能保证工作,但可能会。 w3.org/TR/xslt#section-Creating-Elements-with-xsl:element "XSLT 处理器在选择用于将创建的元素输出为 XML 的前缀时,可以使用 name 属性中指定的 QName 的前缀;但是,它们不是必须这样做的。"。
  • @Mads-Hansen:我检查了三个不同的 XSLT 处理器(只有三个,因为我正在旅行并使用我女儿的笔记本电脑),它们都使用前缀没有问题。
  • 所以,这就是为什么我在过去几天里能够在你面前偷偷地回答一些问题!关于保留前缀,我怀疑它几乎可以在每个处理器中使用。我曾与 Ken Holman 一起工作并参加过他的课程,我记得他曾指出这一点——但他有时会承认自己很迂腐。还为类似问题提供了类似的解决方案:stylusstudio.com/xsllist/200309/post80960.html
  • @Mads-Hansen:是的,Ken 解释得很好。现在我明白了为什么你在 XSLT 方面如此出色——接受 Ken Holman 节目的培训。
  • 这也是一个优雅的解决方案。谢谢迪米特。我现在正在评估我应该为更复杂的(多级)输入 XML 做什么。看起来我会和你一起去。一旦我确定了,我会发布结果。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多