删除命名空间并使用XSL提取XML文件的子集

时间:2011-07-04 20:52:08

标签: xml xslt xml-namespaces

当我的输入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>

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

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

2 个答案:

答案 0 :(得分:3)

这是XPath,XML和XSLT上最常见的FAQ。搜索“默认命名空间和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元素。此处的操作是在所有子节点上应用模板。

  4. 更新: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>
    

答案 1 :(得分:0)

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

 <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处理器版本4.0 进行测试,它提供了:

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