XSLT列出属性(按它们在xml文件中出现的顺序)

时间:2019-04-15 12:51:40

标签: xslt

我有大量的xml文件,其结构类似于以下内容,尽管它们要大得多:

<?xml version="1.0" encoding="UTF-8"?>
<a a1="3.0" a2="ABC">
  <b b1="P1" b2="123">first
  </b>
  <b b1="P2" b2="456" b3="xyz">second
  </b>
</a>

我想得到以下输出:

1|1|b1
1|2|b2
2|1|b1
2|2|b2
2|3|b3

其中:

  1. 字段1是节点/ a / b的序列号
  2. 字段2是该属性在xml文件中显示的序列号
  3. 字段3是属性名称(不是值)

我不太清楚如何正确计算字段2。

我已经准备了以下xslt文件:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <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="/">
  <xsl:for-each select="a/b/@*">
   <xsl:value-of select="count(../preceding-sibling::*)+1"/>
   <xsl:text>|</xsl:text>
   <!-- TODO: This is not correct -->
   <xsl:value-of select="count(preceding-sibling::*)+1"/>
   <xsl:text>|</xsl:text>
   <xsl:value-of select="name()"/>
   <xsl:text>&#10;</xsl:text>
  </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

但是当我运行以下命令时:

xsltproc a.xslt a.xml > a.csv

我得到的输出不正确,因为字段2不代表属性序列号:

1|1|b1
1|1|b2
2|1|b1
2|1|b2
2|1|b3

关于如何获得正确的输出,您有任何建议吗?

请注意,XSLT to order attributes中提供的答案不能解决此问题。

在XML中,属性的顺序无关紧要。例如,<a a1="3.0" a2="ABC"><a a1="3.0" a2="ABC">是等效的。

但是,这个特定问题是大型应用程序的一部分,在该应用程序中,必须确定属性在给定xml文件中的显示顺序(而不是在等效于它们的xml文件中)。

2 个答案:

答案 0 :(得分:2)

尽管,正如kjhughes在评论中所述,属性顺序并不重要。但是,您仍然可以选择它们,并使用position()元素来获取所需的数字(只是不能确定它们的输出顺序将是它们在XML中出现的顺序,尽管通常这样做是会是这种情况。)

尝试此XSLT。请注意xsl:for-each的嵌套用法,以便在获取属性之前先仅选择b个元素,以获取位置,然后再获取具有各自独立位置的属性。

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

 <xsl:template match="/">
   <xsl:for-each select="a/b">
     <xsl:variable name="bPosition"  select="position()"/>
     <xsl:for-each select="@*"> 
       <xsl:value-of select="$bPosition"/>
       <xsl:text>|</xsl:text>
       <xsl:value-of select="position()"/>
       <xsl:text>|</xsl:text>
       <xsl:value-of select="name()"/>
       <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

您可以在要迭代的属性序列中使用项目的position(),并结合逻辑来确定其父元素的位置。

<xsl:template match="/">
    <xsl:for-each select="a/b/@*">
        <xsl:value-of select="count(../preceding-sibling::*)+1"/>
        <xsl:text>|</xsl:text>
        <!-- TODO: This is not correct -->
        <xsl:value-of select="position() - 
               (if (count(../preceding-sibling::*)) then count(../preceding-sibling::*)+1 else 0)"/>
        <xsl:text>|</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

哪个会产生以下输出:

1|1|b1
1|2|b2
2|1|b1
2|2|b2
2|3|b3