计算前面的兄弟姐妹和评论的总数

时间:2019-04-15 10:27:06

标签: xslt

我有许多类似于以下a.xml文件的文件,尽管它们要大得多:

<?xml version="1.0" encoding="UTF-8"?>
<a version="3.0">
  <b bb="P1">
    <!--============== b:P1 c:1 ==============-->
    <c cc="1">
      <d dd="61">d1
      </d>
    </c>
    <!--============== b:P1 c:2 ==============-->
    <c cc="2">
      <d dd="17">d2
      </d>
    </c>
  </b>
</a>

对于每个c,前面只有一个注释。

我要输出的文件与以下a.csv文件的结构相同:

1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d
3|1|a|1|b|3|!|0|
3|1|a|1|b|4|c|0|
4|1|a|1|b|4|c|1|d

它代表a.xml的层次树:

  1. 字段1是层次结构级别。例如,a具有1级,b具有2级,依此类推。

  2. 字段2、4、6和8等于:

    • 如果当前节点的级别小于当前字段的级别,则为0
    • 其他先前的同级兄弟和评论总数加一个
  3. 字段3、5、7和9等于:

    • 如果当前节点的级别小于当前字段的级别,则为“”
    • 否则“!”如果当前节点之前带有注释或节点名称

在此示例中,级别3包含注释。

我找不到一种很好的方法来进行同时包含节点和注释的for-each。当我使用<xsl:for-each select="*">时,我只会遍历节点。

因此,我提出了以下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="*">
   <xsl:variable name="elm01" select="local-name()" />
   <xsl:text>1</xsl:text>
   <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
   <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
   <xsl:text>|0| |0| |0|</xsl:text>
   <xsl:text>&#10;</xsl:text>
   <xsl:for-each select="*">
    <xsl:variable name="elm02" select="local-name()" />
    <xsl:text>2</xsl:text>
    <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
    <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
    <xsl:text>|0| |0|</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:for-each select="*">
     <xsl:variable name="elm03" select="local-name()" />
     <xsl:if test="preceding-sibling::comment()[1]">
      <xsl:text>3</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
      <xsl:text>|!</xsl:text>
      <xsl:text>|0|</xsl:text>
      <xsl:text>&#10;</xsl:text>
     </xsl:if>
     <xsl:text>3</xsl:text>
     <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
     <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
     <!-- TODO: I want to count the total of preceding siblings and comments -->
     <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|comment())+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
     <xsl:text>|0|</xsl:text>
     <xsl:text>&#10;</xsl:text>
     <xsl:for-each select="*">
      <xsl:variable name="elm04" select="local-name()" />
      <xsl:text>4</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
      <xsl:text>&#10;</xsl:text>
     </xsl:for-each>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

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

xsltproc a.xslt a.xml > a.csv

我得到以下a.csv文件:

1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|1|c|0|
4|1|a|1|b|1|c|1|d
3|1|a|1|b|2|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d

请注意,字段6不正确:

  • 第一个注释以及第一个节点c及其子节点都等于1

  • 第二个注释以及第二个节点c及其子节点都等于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="*">
   <xsl:variable name="elm01" select="local-name()" />
   <xsl:text>1</xsl:text>
   <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
   <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
   <xsl:text>|0| |0| |0|</xsl:text>
   <xsl:text>&#10;</xsl:text>
   <xsl:for-each select="*">
    <xsl:variable name="elm02" select="local-name()" />
    <xsl:text>2</xsl:text>
    <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
    <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
    <xsl:text>|0| |0|</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:for-each select="*">
     <xsl:variable name="elm03" select="local-name()" />
     <xsl:if test="preceding-sibling::comment()[1]">
      <xsl:text>3</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())"/>
      <xsl:text>|!</xsl:text>
      <xsl:text>|0|</xsl:text>
      <xsl:text>&#10;</xsl:text>
     </xsl:if>
     <xsl:text>3</xsl:text>
     <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
     <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
     <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
     <xsl:text>|0|</xsl:text>
     <xsl:text>&#10;</xsl:text>
     <xsl:for-each select="*">
      <xsl:variable name="elm04" select="local-name()" />
      <xsl:text>4</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*|../preceding-sibling::comment())+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
      <xsl:text>&#10;</xsl:text>
     </xsl:for-each>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

或者,您也可以在蒂姆的回复中使用xslt,从而避免重复。

1 个答案:

答案 0 :(得分:1)

您想要的表情是这个...

<xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment()) + 1" />

或者这也可以工作...

<xsl:value-of select="count(preceding-sibling::node()[self::*|self::comment()]) + 1" />

但是您也可以使用xsl:number

<xsl:number count="*|comment()" />

尽管如此,您的样式表似乎有些过于复杂,而且重复很多。尝试使用这种更通用的方法。递归地调用每个级别,每次调用都传递到构造的行中,以节省每次构建它的时间。

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

  <xsl:param name="maxLevel" select="4" />

  <xsl:template match="*|comment()">
    <xsl:param name="level" select="1" />
    <xsl:param name="prev" />
    <xsl:variable name="new">
        <xsl:value-of select="$prev" />
        <xsl:text>|</xsl:text>
        <xsl:number count="*|comment()" />
        <xsl:text>|</xsl:text>
        <xsl:choose>
            <xsl:when test="self::*">
                <xsl:value-of select="local-name()" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>!</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>

    <xsl:value-of select="$level" />
    <xsl:value-of select="$new" />
    <xsl:call-template name="pad">
        <xsl:with-param name="levels" select="$maxLevel - $level" />
    </xsl:call-template>

    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="*|comment()">
        <xsl:with-param name="level" select="$level + 1" />
        <xsl:with-param name="prev" select="$new" />
    </xsl:apply-templates>        
  </xsl:template>

  <xsl:template name="pad">
      <xsl:param name="levels" />
      <xsl:if test="$levels > 0">
          <xsl:text>|0| </xsl:text>
          <xsl:call-template name="pad">
              <xsl:with-param name="levels" select="$levels - 1" />
          </xsl:call-template>
      </xsl:if>
  </xsl:template>
</xsl:stylesheet>

http://xsltfiddle.liberty-development.net/jyRYYiy上查看它的运行情况