首页 > 解决方案 > 建立“目录”

问题描述

考虑以下'sample.xml'

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<level>
    <name>testA</name>
    <level>
        <name>testB</name>  
    </level>
    <level>
        <name>testC</name>  
        <level>
            <name>testD</name>  
            <level>
                <name>testE</name>  
            </level>
        </level>
    </level>
</level>
</root>

使用 xmlstarlet 我可以做到:

xml sel -t -m //level -v name -o " " -v "count(ancestor::*)-1" -o "." -v "count(preceding-sibling::*)" -n sample.xml

这会产生:

testA 0.0
testB 1.1
testC 1.2
testD 2.1
testE 3.1

我应该怎么做才能得到:

testA 0.0
testB 1.1
testC 1.2
testD 1.2.1
testE 1.2.1.1

在这个例子中,我只有 4 个级别,但这可以超过 4 个。

我正在考虑某种递归,是否有任何可用的链接可以解释如何做到这一点?

标签: xpathxmlstarlet

解决方案


您应该能够使用 XSLT 和xmlstarlet 中的“tr”命令来执行此操作...

但是,您想要的输出有点令人困惑。如果“testA”是第一个级别并且您从零开始,为什么所有其他条目不从零开始?或者也许“根”应该为零?

无论如何,这是一个从 1 而不是 0 开始的示例,应该可以帮助您入门...

XML 输入(input.xml)

<root>
    <level>
        <name>testA</name>
        <level>
            <name>testB</name>  
        </level>
        <level>
            <name>testC</name>  
            <level>
                <name>testD</name>  
                <level>
                    <name>testE</name>  
                </level>
            </level>
        </level>
    </level>
</root>

XSLT 1.0 (test.xsl)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="level">
    <xsl:value-of select="concat(name, ' ')"/>
    <xsl:for-each select="ancestor-or-self::level">
      <xsl:if test="not(position()=1)">.</xsl:if>
      <xsl:number/>
    </xsl:for-each>
    <xsl:text>&#xA;</xsl:text>
    <xsl:apply-templates select="level"/>
  </xsl:template>

</xsl:stylesheet>

命令行

xmlstarlet tr test.xsl input.xml

输出

testA 1
testB 1.1
testC 1.2
testD 1.2.1
testE 1.2.1.1

推荐阅读