【问题标题】:Remove Namespace and Extract a subset of XML file using XSL使用 XSL 移除命名空间并提取 XML 文件的子集
【发布时间】:2011-07-04 20:52:07
【问题描述】:

当我的输入 Xml 为:

 <country>
       <state>
           <city>
               <name>DELHI</name>            
           </city>
      </state>
    </country>

对于所需的输出如下:

<city>
  <name>DELHI</name>            
</city

以下 xsl 工作正常:

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

但如果添加了名称空间,则相同的 XSL 不适用于上述输入 XML: 如下:

<country xmlns="http://india.com/states" version="1.0">
   <state>
       <city>
           <name>DELHI</name>            
       </city>
  </state>
</country>

我希望删除名称空间以及要复制的城市元素。

任何帮助将不胜感激。 谢谢

【问题讨论】:

  • 好问题,+1。请参阅我的答案以获得完整,简短且简单的解决方案。还提供了详细的解释。
  • 我更新了对“另一个约翰”的修改问题的答案。如果是您,请联系 SO 以合并您的两个身份。

标签: xml xslt xml-namespaces


【解决方案1】:

这是关于 XPath、XML 和 XSLT 的最常见问题解答。搜索“默认命名空间和 XPath 表达式”。

至于解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:x="http://india.com/states">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="*">
  <xsl:element name="{name()}">
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates/>
  </xsl:element>
 </xsl:template>


 <xsl:template match="*[not(ancestor-or-self::x:city)]">
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时

<country xmlns="http://india.com/states" version="1.0">
    <state>
        <city>
            <name>DELHI</name>
        </city>
    </state>
</country>

产生想要的结果

<city>
   <name>DELHI</name>
</city>

解释

  1. 在 XPath 中,无前缀的元素名称始终被视为“无命名空间”。但是,所提供的 XML 文档中的每个元素名称都位于非空命名空间(默认命名空间 "http://india.com/states")中。因此,//city 不选择节点(因为 XML 文档中没有任何元素不是命名空间),而 //x:city 其中x: 绑定到命名空间 "http://india.com/states" 选择所有城市元素(位于命名空间"http://india.com/states")。

  2. 在此转换中有两个模板。第一个模板匹配任何元素并重新创建它,但在无命名空间中。它还复制所有属性,然后将模板应用到该元素的子节点。

  3. 对于不是city 元素的祖先或本身不是city 元素的所有元素,第二个模板覆盖第一个模板。此处的操作是在所有子节点上应用模板。

更新:OP 已修改问题,询问为什么在处理新的、已修改的 XML 文档的结果中有不需要的文本:

<country xmlns="http://india.com/states" version="1.0">
        <state>
            <city>
                <name>DELHI</name>
            </city>
        </state>
        <state2>
            <city2>
                <name2>MUMBAI</name2>
            </city2>
        </state2>
</country>

为了生成文本“MUMBAI”,上面的转换需要稍微修改——忽略(而不是复制)任何没有x:city祖先的文本节点。为此,我们添加以下一行空模板:

 <xsl:template match="text()[not(ancestor::x:city)]"/>

现在整个转变变成了

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:x="http://india.com/states">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:template match="*">
      <xsl:element name="{name()}">
       <xsl:copy-of select="@*"/>
       <xsl:apply-templates/>
      </xsl:element>
     </xsl:template>

     <xsl:template match="*[not(ancestor-or-self::x:city)]">
      <xsl:apply-templates/>
     </xsl:template>

     <xsl:template match="text()[not(ancestor::x:city)]"/>
</xsl:stylesheet>

结果仍然是想要的,正确的

<city>
   <name>DELHI</name>
</city>

【讨论】:

    【解决方案2】:

    您可以使用以下模板获得所需的输出:

     <xsl:template match="*[not(ancestor-or-self::x:*[starts-with(name(),'city')])]">
      <xsl:apply-templates/>
     </xsl:template>
    

     <xsl:template match="/">
         <xsl:apply-templates select="//x:*[starts-with(name(),'city')]"/>
     </xsl:template>
    

    使用 Microsoft (R) XSLT Processor Version 4.0 对您提供的新输入进行测试:

    <city>
       <name>DELHI</name>
    </city>
    <city2>
       <name2>MUMBAI</name2>
    </city2>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      • 2019-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多