用于计算唯一后代元素的XPath表达式

时间:2012-07-16 10:56:23

标签: xslt xpath

我正在编写一个XPath表达式来计算独特的子代理。使用以下xPath表达式,我可以获取所有子属性以及不唯一的属性:

//*[count(*)=0] 

我需要一个XPath表达式来返回所有唯一属性和唯一属性数量

例如:XML文件

<details>
    <Employee>
        <EmpNo>10</EmpNo>
        <EmpName>TestName</EmpName>
        <Address>
           <Address1>market</Address1>
           <Address2>motel</Address2>
           <Street/>
        </Address>
    </Employee>

    <Employee>
        <EmpNo>20</EmpNo>
        <EmpName>TestName2</EmpName>
        <Address>
           <Address1>school</Address1>
           <Address2>playground</Address2>
           <Street>
                <StreetName>TestStreet2</StreetName>
                <StreetCode>200</StreetCode>
           </Street>
        </Address>
    </Employee>

预期产出:

  <!-- Unique element's count -->
  <data>6</data>
  <!-- Unique Element Names -->
  <data>EmpNo</data>
  <data>EmpName</data>
  <data>Address1</data>
  <data>Address2</data>
  <data>StreetName</data>
  <data>StreetCode</data>
  <!-- Unique Element values -->
  <!-- Data Set 1 -->
  <data>10</data>
  <data>TestName</data>
  <data>market</data>
  <data>motel</data>
  <data>null</data>
  <data>null</data>
  <!-- Data Set 2 -->
  <data>20</data>
  <data>TestName2</data>
  <data>school</data>
  <data>playground</data>
  <data>TestStreet2</data>
  <data>200</data>

感谢。

1 个答案:

答案 0 :(得分:1)

此XSLT 1.0样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="yes" omit-xml-declaration="yes" />

  <!-- index data fields by their element name -->
  <xsl:key 
    name  = "kFields" 
    match = "Employee//*"
    use   = "name()" 
  />

  <!-- store a unique list of elements (Muenchian Grouping) -->    
  <xsl:variable name="fields" select="
    /details/Employee//*[
      generate-id()
      =
      generate-id(key('kFields', name())[1])
    ][
      not(
        key('kFields', name())/*
      )
    ]
  " />

  <!-- main output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
  <xsl:template match="/details">
    <xsl:comment> unique element count </xsl:comment>
    <data>
      <xsl:value-of select="count($fields)" />
    </data>
    <xsl:call-template name="newline" />

    <xsl:comment> unique element names </xsl:comment>
    <xsl:for-each select="$fields">
      <data>
        <xsl:value-of select="name()" />
      </data>
      <xsl:call-template name="newline" />
    </xsl:for-each>

    <xsl:comment> unique element values </xsl:comment>
    <xsl:apply-templates select="Employee" />
  </xsl:template>

  <!-- Employee output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
  <xsl:template match="Employee">
    <xsl:variable name="this" select="." />

    <xsl:comment> data set <xsl:value-of select="position()" /> </xsl:comment>
    <xsl:for-each select="$fields">
      <xsl:variable 
        name="val" 
        select="$this//*[not(*) and name() = name(current())]" 
      />
      <data>
        <xsl:choose>
          <xsl:when test="normalize-space($val) != ''">
            <xsl:value-of select="$val" />
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>null</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </data>
      <xsl:call-template name="newline" />
    </xsl:for-each>
  </xsl:template>

  <!-- Helpers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
  <xsl:template name="newline">
    <xsl:value-of select="'&#xA;'" />
  </xsl:template>

</xsl:stylesheet>

生成(换行符可能会以不同方式重现):

<!-- unique element count -->
<data>6</data>

<!-- unique element names -->
<data>EmpNo</data>
<data>EmpName</data>
<data>Address1</data>
<data>Address2</data>
<data>StreetName</data>
<data>StreetCode</data>

<!-- unique element values -->

<!-- data set 1 -->
<data>10</data>
<data>TestName</data>
<data>market</data>
<data>motel</data>
<data>null</data>
<data>null</data>

<!-- data set 2 -->
<data>20</data>
<data>TestName2</data>
<data>school</data>
<data>playground</data>
<data>TestStreet2</data>
<data>200</data>

注意:

  • //*[count(*)=0]//*[not(*)]相同。后者更好。
  • 我使用<xsl:key> Muenchian分组来计算<Employee>
  • 后代中的唯一元素名称
  • 变量$fields中的XPath表达式做了两件事:
    • 首先,它使用元素的Muenchian分组,使name()成为唯一元素。
    • 然后检查剩余的元素。输入中的任何位置都不能包含任何相同名称的元素(not( key('kFields', name())/* )。否则<data>Street</data>会显示在输出中。
  • 您的输出格式不明确。如果有些名称相同但嵌套位置不同的元素,事情就会搞砸了。