我正在尝试使用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实现这一目标?有什么帮助吗?
答案 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)任何不具有属于前缀ns1:
所绑定的名称空间的属性的元素,并且它本身也不属于该名称空间,也是它没有属于该命名空间的后代元素节点。 2)任何不属于该命名空间的属性。
记住:覆盖身份规则是最基本,最强大的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}}这样的情况。