如何用XSLT区分XML自闭合<hello/>与空内容<hello></hello>标签?
区分XML自闭合标签与空内容标签的XSLT解决方案
首先得给你提个醒:在标准XML的语义里,<hello/>和<hello></hello>是完全等价的——XML解析器会把它们转换成完全相同的元素节点(没有任何子节点),所以纯标准的XSLT没办法直接区分这两种语法写法。但既然客户有明确的需求,我们还是有办法实现的,下面分两种场景给你解决方案:
场景1:严格区分语法写法(必须识别自闭合标签的写法)
这种情况需要绕开标准的XML节点解析,直接处理原始的XML文本,或者借助XSLT处理器的扩展能力:
方案A:用XSLT 3.0处理原始XML文本
通过unparsed-text读取原始XML文件,再用正则表达式匹配不同的标签写法。注意:这种方法只适合简单的XML结构,如果标签有属性、嵌套结构,正则很容易失效。
示例代码:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="/"> <!-- 读取原始XML文本 --> <xsl:variable name="raw-xml" select="unparsed-text('your-input.xml')"/> <!-- 匹配hello标签的两种写法 --> <xsl:analyze-string select="$raw-xml" regex="<hello(/?)>(?:</hello>)?" flags="i"> <xsl:matching-substring> <xsl:choose> <!-- 匹配自闭合标签 --> <xsl:when test="regex-group(1) = '/'"> "hello": null </xsl:when> <!-- 匹配空内容的开始/结束标签 --> <xsl:otherwise> "hello": "" </xsl:otherwise> </xsl:choose> </xsl:matching-substring> <!-- 处理XML中的其他内容,这里省略 --> </xsl:analyze-string> </xsl:template> </xsl:stylesheet>
方案B:借助处理器扩展(比如Saxon)
一些XSLT处理器(比如Saxon)支持扩展功能,可以序列化节点并控制输出格式,或者获取节点的原始语法信息。比如用XSLT 3.0的标准serialize函数,强制序列化时保留原始的标签形式(需要处理器支持相关参数):
示例代码:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="hello"> <!-- 序列化当前节点,关闭自动缩进,强制保留空元素的写法 --> <xsl:variable name="serialized-node" select="serialize(., map{ 'method': 'xml', 'indent': false(), 'omit-xml-declaration': true(), 'use-empty-elements': false() <!-- 这个参数强制输出<hello></hello>而非自闭合 --> })"/> <xsl:choose> <!-- 如果序列化结果是自闭合标签(说明原节点是自闭合写法) --> <xsl:when test="$serialized-node = '<hello/>'"> "hello": null </xsl:when> <xsl:otherwise> "hello": "" </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
注意:use-empty-elements参数的支持取决于处理器,部分处理器可能需要调整参数名称。
场景2:退而求其次——区分“无内容”与“空文本内容”
如果客户可以接受稍微调整需求(比如把<hello></hello>理解为包含空文本节点的元素),可以通过判断元素是否有子节点来实现:
示例代码:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="UTF-8"/> <xsl:template match="hello"> <xsl:choose> <!-- 没有任何子节点(对应自闭合标签,或无空白的<hello></hello>) --> <xsl:when test="not(node())"> "hello": null </xsl:when> <!-- 有子节点但内容为空(比如包含空白的<hello></hello>) --> <xsl:when test="normalize-space(.) = ''"> "hello": "" </xsl:when> <!-- 有实际内容的情况 --> <xsl:otherwise> "hello": "<xsl:value-of select="."/>" </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
这种方法的前提是:你的XML解析器会保留<hello></hello>中的空文本节点(可以通过xsl:preserve-space或解析器配置控制)。
内容的提问来源于stack exchange,提问作者Aprendiendo Siempre
相关产品推荐
相关产品推荐

