XSLT 1.0:如何仅复制含值或后代含值的元素
过滤XML中无有效内容的元素(自身及后代均无值)
现有如下XML文档:
<?xml version="1.0" encoding="utf-8"?> <Products> <Product> <Sku /> <Suppliers> <Supplier> <Name /> </Supplier> </Suppliers> <Priority>1</Priority> </Product> <Product> <Sku>123</Sku> <Suppliers> <Supplier>Jon</Supplier> </Suppliers> <Priority>3</Priority> <e /> </Product> </Products>
需要通过XSLT转换,仅输出自身含值或后代元素含值的节点。当前使用的过滤模板通过match="*[not(node())]"仅能过滤无后代的空元素,无法处理包含空后代的元素(比如示例中的<Suppliers>和<Supplier>):
<xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <!-- When matching empty: do nothing --> <xsl:template match="*[not(node())]"> <xsl:comment>filtering <xsl:value-of select="local-name()"/></xsl:comment> </xsl:template>
当前输出(含调试注释)
<?xml version="1.0" encoding="utf-8"?><Products> <Product> <!--filtering Sku--> <Suppliers> <Supplier> <!--filtering Name--> </Supplier> </Suppliers> <Priority>1</Priority> </Product> <Product> <Sku>123</Sku> <Suppliers> <Supplier>Jon</Supplier> </Suppliers> <Priority>3</Priority> <!--filtering e--> </Product> </Products>
期望输出(无注释)
<?xml version="1.0" encoding="utf-8"?><Products> <Product> <Priority>1</Priority> </Product> <Product> <Sku>123</Sku> <Suppliers> <Supplier>Jon</Supplier> </Suppliers> <Priority>3</Priority> </Product> </Products>
解决方案
核心思路是:判断元素自身及所有后代是否包含有效内容(非空白文本),如果完全没有则过滤该元素。修改后的XSLT如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 身份模板:默认复制所有节点和属性 --> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <!-- 过滤自身及后代均无有效内容的元素 --> <xsl:template match="*[not(normalize-space(.))]"/> </xsl:stylesheet>
说明:
normalize-space(.)会将元素自身及所有后代的文本内容合并,去除首尾空白并压缩中间空白。如果结果为空,说明该元素及其所有后代都没有非空白的有效文本。- 匹配这类元素的模板不执行任何复制操作,直接过滤掉它们,最终只保留包含有效内容的元素及其父元素(父元素如果有有效后代则会被保留)。
这个方案会递归判断元素的所有后代,自动过滤掉所有空的元素分支,完全符合期望输出的要求。
内容的提问来源于stack exchange,提问作者Jonny Hotchkiss
相关产品推荐
相关产品推荐

