XSL: Counting Previous Unique Siblings from child

2019-07-29 14:00发布

I want to apply a XSL style sheet that counts the previous unique "ROLE" nodes and spits out the following output format of ROLE/@name, PERM/@perfrom and the number of unique ROLE nodes prior to the current node.
This is a continuation from this question but with a bit more complexity. I believe the Muenchian method is the best way to implement this because the length of this file will be large.

I have the following XML (sorry about the length)

<?xml version="1.0" encoding="utf-8" ?>
<ROLEACTIONINFO>
  <ROLE name="TESTER">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT0" field="ALL" permfrom="PERM1565"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT1">
    <ACTIONINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT1">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT8" field="ALL" permfrom="PERM1"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT1">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT7" field="ALL" permfrom="PERM2"/>
        <PERM type="PT7" field="ALL" permfrom="PERM54"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT2">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT6" field="ALL" permfrom="PERM1"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT2">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT5" field="ALL" permfrom="PERM2"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT3">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT2" field="ALL" permfrom="PERM44"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
 </ROLEACTIONINFO>

Here is a version XSL sheet I have been playing with:

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

  <xsl:key name="kRole" match="ROLE[ACTIONINFO/PERMINFO/PERM]" use="@name" />

  <xsl:template match="PERM">
    <xsl:variable name="roles-so-far" select="ancestor::ROLE | ancestor::ROLE/preceding-sibling::ROLE[ACTIONINFO/PERMINFO/PERM]"/>
    <!-- Only select the first instance of each ROLE name -->
    <xsl:variable name="roles-so-far-unique"              
                   select="$roles-so-far[generate-id(ancestor::ROLE) = generate-id(key('kRole',ancestor::ROLE/@name)[1])]"/>
    <xsl:apply-templates select="ancestor::ROLE/@name"/>
    <xsl:text>&#x9;</xsl:text>
    <xsl:apply-templates select="@permfrom"/>
    <xsl:text>&#x9;</xsl:text>
    <xsl:value-of select="count($roles-so-far-unique)"/>
    <!-- linefeed -->
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

Here is the desired output:

TESTER  PERM1565    1
PARENT1 PERM1   2
PARENT1 PERM2   2
PARENT1 PERM54  2
PARENT2 PERM1   3
PARENT2 PERM2   3
PARENT3 PERM44  4

Here is the actual (incorrect) output:

TESTER  PERM1565    1
PARENT1 PERM1   2
PARENT1 PERM2   3
PARENT1 PERM54  3
PARENT2 PERM1   4
PARENT2 PERM2   5
PARENT3 PERM44  6

Thanks in advance.

标签: xml xslt
1条回答
聊天终结者
2楼-- · 2019-07-29 14:34

This goes from every <PERM>, taking the according <ROLE> and counting it's unique predecessors:

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

  <xsl:output method="text" />

  <xsl:variable name="TAB" select="'&#x09;'" />
  <xsl:variable name="LF"  select="'&#x0A;'" />

  <xsl:key name="kRole" match="ROLE" use="@name" />

  <xsl:template match="/">
    <xsl:apply-templates select="//PERM" />
  </xsl:template>

  <xsl:template match="PERM">
    <xsl:variable name="vThisRole" select="ancestor::ROLE[1]" />
    <xsl:variable name="vPrecedingRoles" select="
      ($vThisRole | $vThisRole/preceding-sibling::ROLE)
    " />
    <xsl:variable name="vUniquePrecedingRoles" select="
      $vPrecedingRoles[count(. | key('kRole', ./@name)[1]) = 1]
    " />
    <xsl:value-of select="
      concat(
        $vThisRole/@name, $TAB,
        @permfrom, $TAB,
        count($vUniquePrecedingRoles), $LF
      )
    " />
  </xsl:template>

</xsl:stylesheet>

Output:

TESTER  PERM1565        1
PARENT1 PERM1   2
PARENT1 PERM2   2
PARENT1 PERM54  2
PARENT2 PERM1   3
PARENT2 PERM2   3
PARENT3 PERM44  4

The XML you are dealing with is in a pretty bad shape, if you ask me. At least the fact that anything security relevant is expressed by "the count of preceding unique names" instead of a proper data structure makes me scratch my head. ;-) Any chance you can change the input?

查看更多
登录 后发表回答