Concise expression for counting leaf nodes in spec

2019-08-05 07:01发布

问题:

In my attempt to answer question XSLT - Tricky Transformation I came across the following task:

For each tag <MMM> count the number of leaf nodes in the second <MMM> child tag.

Define "leaf node" as an <MMM> tag that does not have other <MMM> tags as children. It may have other tags as children, though.

I ended up with two expressions:

<xsl:variable name="just_children" select="count(MMM[position() = 2 and count(MMM) = 0])"/>
<xsl:variable name="grandchildren_and_below" select="count(MMM[2]//MMM[count(MMM) = 0])"/>

The first one counts the leafs in the second child, the second one in all ancestors of the second child. I was not able to combine these two into one expression (except for the obvious summing which I have to do :-) ). Somehow I cannot make the node array index selector [2], the additional condition count(MMM) = 0, and the ancestor path `//' fit into the same expression.

So, is there more a concise (and probably more elegant) way to write this expression?

Thanks a lot!

For testing you can use this input file:

<?xml version="1.0" encoding="ISO-8859-1"?>
<MMM>
  <MMM>
    <MMM>
      <MMM/>
      <MMM/>
    </MMM>    
    <MMM>
      <MMM/>
      <MMM>
        <MMM/>
      </MMM>
    </MMM>
  </MMM>
  <MMM>
    <MMM/>
  </MMM>
</MMM>

and the following XSLT sheet:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>

  <xsl:template match="MMM">
    <xsl:variable name="just_children" select="count(MMM[position() = 2 and count(MMM) = 0])"/>
    <xsl:variable name="grandchildren_and_below" select="count(MMM[2]//MMM[count(MMM) = 0])"/>
    <MMM just_children="{$just_children}" grandchildren_and_below="{$grandchildren_and_below}">
      <xsl:apply-templates/>
    </MMM>    
  </xsl:template>
</xsl:stylesheet>

回答1:

Your just_children variable could be written as this:

<xsl:variable name="just_children" select="count(MMM[2][not(MMM)])"/>

And your grandchildren_and_below variable could be written as this:

<xsl:variable name="grandchildren_and_below" select="count(MMM[2]//MMM[not(MMM)])"/>

To put them both together as one, you could use the descendant-or-self axes, like so

<xsl:variable name="all" select="count(MMM[2]//descendant-or-self::MMM[not(MMM)])"/>


标签: xslt xpath