【问题标题】:Flattening attributes of nested XML elements using XSLT使用 XSLT 扁平化嵌套 XML 元素的属性
【发布时间】:2018-03-12 19:50:53
【问题描述】:

我正在尝试通过使用 XSLT 组合属性的值来展平嵌套 XML 元素的树。例如,如果我有以下输入:

<node value="a">
    <node value="b">
        <node value="c">
            <node value="d">
            </node>
        </node>
        <node value="e">
            <node value="f">
            </node>
            <node value="g">
                <node value="h">
                </node>
            </node>
        </node>
    </node>
</node>

那么这些将是我希望能够得到的“扁平化”结果:

a/b/c/d
a/b/e/f
a/b/e/g/h

我目前所能实现的只是在具有“value”属性的节点“node”的最深嵌套出现处输出记录:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv">
    <xsl:output method="text" encoding="utf-8" />
    <xsl:template match="text()|@*"/>
    <xsl:template match="node[@value]">
        <xsl:if test="not(descendant::node[@value])">
            <xsl:value-of select="@value"/>
            <xsl:text>&#xa;</xsl:text>
        </xsl:if>
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

您可能从我的描述和xsl:if 测试中了解到,一个潜在的复杂情况是“node”元素的某些实例可能没有“value”属性,因此必须明确检查。如何更新此样式表以达到预期的效果?

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    使用 XSLT 2 或 3,它变成了

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="3.0">
    
      <xsl:output method="text"/>
    
      <xsl:template match="/">
          <xsl:value-of select="descendant::node[@value and not(descendant::node[@value])]!string-join(ancestor-or-self::node/@value, '/')" separator="&#10;"/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/b4GWVh/0

    我会使用 XSLT 1

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
    
      <xsl:output method="text"/>
    
      <xsl:template match="/">
          <xsl:apply-templates select="descendant::node[@value and not(descendant::node[@value])]"/>
      </xsl:template>
    
      <xsl:template match="node">
          <xsl:apply-templates select="ancestor-or-self::node/@value"/>
          <xsl:text>&#10;</xsl:text>
      </xsl:template>
    
      <xsl:template match="@value">
          <xsl:if test="position() > 1">/</xsl:if>
          <xsl:value-of select="."/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/b4GWVh/1

    【讨论】:

    • 这帮助我找到了一个似乎有效的解决方案(使用您的第二种方法,因为xsltproc 仅限于 XSLT 1.0),但我必须先对其进行一些修改。照原样,它适用于直接示例,但 afaik 无法让我保留对我可能想要处理的相应“节点”元素的任何其他属性的访问。我不得不将属性模板的value-of 推回到节点模板中(现在在apply-templates 中需要last())并根据另一个“我是最后一个后代”测试(@987654329)将分隔符/换行符放在选择块中@ 似乎不再起作用了)。
    • 我不确定这里的正确礼仪是什么——我应该将修改作为评论留在这里,以供未来的读者分享该需求、接受答案并走开,还是应该更新问题并让您同样更新答案...?
    • 恐怕您的问题描述根本没有显示任何其他属性,也没有说明您想要输出它们的位置。在与元素的任何属性匹配的模板中,例如@value,您当然可以导航到../@foo 以选择同一元素的foo 属性。
    • 如果你想处理 node 元素两次而不是处理 value 属性,那么你可以使用一种模式,例如&lt;xsl:apply-templates select="ancestor-or-self::node/." mode="atts"/&gt; 然后输出例如&lt;xsl:template match="node" mode="atts"&gt;&lt;xsl:if test="position() &gt; 1"&gt;/&lt;/xsl:if&gt;&lt;xsl:value-of select="concat(@value, ',', @foo)"/&gt;&lt;/xsl:template&gt;.
    • 对我来说,一个简单的边界 hello world 样式输入有 60k 行长,所以我只是试图创建一个直接解决我正在处理的特定问题的最小示例。对于后代,我最初对您的答案所做的修改如下所示:
    猜你喜欢
    • 2014-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    • 2017-09-18
    相关资源
    最近更新 更多