XSLT删除属于命名空间的标记/属性

时间:2011-06-10 20:43:40

标签: xslt

我正在尝试使用XSLT删除属于命名空间的标签/属性。困难在于来自不同名称空间的标签可以相互嵌入。

样品:

<?xml version="1.0" encoding="utf-8"?>
<Collection xmlns="http://s0" xmlns:ns1="http://s1">
  <Identifier Name="CollectionX"
          ns1:GlobalID="{E436833B-B0A6-4E0D-804B-60052B767AE3}"
          ns1:LocalID="{0130C866-7A91-4544-A82B-E0C0F2E3BCB2}"  />

  <Properties>
    <ns1:Collectible>1982</ns1:Collectible>
    <Displayed>Reserved</Displayed>
    <Picture>Reserved.jpeg</Picture>
  </Properties>

  <WeakLinks>
    <Link Type="resource" Language="en-us"/>
  </WeakLinks>

</Collection>

我想过滤掉所有不属于ns1的标签/属性,只要它们没有任何ns1子级。

所以结果应该是:

<?xml version="1.0" encoding="utf-8"?>
<Collection xmlns="http://s0" xmlns:ns1="http://s1">
  <Identifier 
      ns1:GlobalID="{E436833B-B0A6-4E0D-804B-60052B767AE3}"
      ns1:LocalID="{0130C866-7A91-4544-A82B-E0C0F2E3BCB2}"  />

  <Properties>
    <ns1:Collectible>1982</ns1:Collectible>
  </Properties>

</Collection>

我如何通过XSLT实现这一目标?有什么帮助吗?

2 个答案:

答案 0 :(得分:6)

此转化

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

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

 <xsl:template match=
 "*[not(attribute::ns1:*)
  and
    not(descendant-or-self::ns1:*)
   ]
 |
  @*[not(namespace-uri()='http://s1')]
 "/>
</xsl:stylesheet>

应用于提供的XML文档时:

<Collection xmlns="http://s0" xmlns:ns1="http://s1">
    <Identifier Name="CollectionX"
      ns1:GlobalID="{E436833B-B0A6-4E0D-804B-60052B767AE3}"
      ns1:LocalID="{0130C866-7A91-4544-A82B-E0C0F2E3BCB2}"  />
    <Properties>
        <ns1:Collectible>1982</ns1:Collectible>
        <Displayed>Reserved</Displayed>
        <Picture>Reserved.jpeg</Picture>
    </Properties>
    <WeakLinks>
        <Link Type="resource" Language="en-us"/>
    </WeakLinks>
</Collection>

生成想要的正确结果

<Collection xmlns="http://s0" xmlns:ns1="http://s1">
   <Identifier ns1:GlobalID="{E436833B-B0A6-4E0D-804B-60052B767AE3}"
   ns1:LocalID="{0130C866-7A91-4544-A82B-E0C0F2E3BCB2}"/>
   <Properties>
      <ns1:Collectible>1982</ns1:Collectible>
   </Properties>
</Collection>

<强>解释

  1. 身份规则(模板)按“原样”复制每个节点。

  2. 只有一个模板会覆盖身份规则。此模板没有正文 - 这意味着它可以有效地过滤(删除)任何匹配的节点,使其不被复制到输出。匹配的节点正是必须过滤掉的节点:1)任何不具有属于前缀ns1:所绑定的名称空间的属性的元素,并且它本身也不属于该名称空间,也是它没有属于该命名空间的后代元素节点。 2)任何不属于该命名空间的属性。

  3. 记住:覆盖身份规则是最基本,最强大的XSLT设计模式。有关此设计模式的更多信息,请参见 here

答案 1 :(得分:1)

您可以使用ns1选择具有ns1:*命名空间的元素。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:ns1="http://s1">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@ns1:* | 
                                   node()[attribute::ns1:* | 
                                          descendant-or-self::ns1:*] | 
                                   text() | comment() | processing-instruction()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

<强>更新

我将XPath更新为匹配@ns1:*的属性,只捕获具有所需命名空间的属性。我还在测试中修复了对注释和处理指令的支持。给出以下XML

<?xml version="1.0" encoding="utf-8"?>
<Collection xmlns="http://s0" xmlns:ns1="http://s1">
  <Identifier Name="CollectionX"
          ns1:GlobalID="{E436833B-B0A6-4E0D-804B-60052B767AE3}"
          ns1:LocalID="{0130C866-7A91-4544-A82B-E0C0F2E3BCB2}"  />
<!-- comment -->
  <Properties>
    <ns1:Collectible>1982</ns1:Collectible>
    <Displayed>Reserved</Displayed>
    <Picture>Reserved</Picture>
  </Properties>

  <WeakLinks>
    <Link Type="resource" Language="en-us"/>
  </WeakLinks>

</Collection>

上面的XSL产生了这个输出(用Saxon和MSXML测试)。

<?xml version="1.0" encoding="UTF-8"?>
<Collection xmlns="http://s0" xmlns:ns1="http://s1">
  <Identifier ns1:GlobalID="{E436833B-B0A6-4E0D-804B-60052B767AE3}" 
              ns1:LocalID="{0130C866-7A91-4544-A82B-E0C0F2E3BCB2}"/>
  <!-- comment -->
  <Properties>
    <ns1:Collectible>1982</ns1:Collectible>
  </Properties>
</Collection>

更新2

我删除了之前对no-namespace的属性的引用。 According to the XPath specification summarized very well here by @Dimitre.Novatchev,没有名称空间前缀的属性属于“no-namespace”,不是默认名称空间或父节点的名称空间。如果您确实想要匹配,请将@*[parent::ns1:* and namespace-uri()=''] |添加到<apply-templates ...>中的匹配表达式。这将适用于您希望匹配<ns1:Collectible WhatIsMyNamespace="no-namespace">的{​​{1}}这样的情况。