确定唯一的子节点数XSLT

时间:2016-08-01 21:46:52

标签: xml xslt

我已经四处搜索,发现这个网站上有很多帖子对我这么做很有帮助。

我正在尝试使用xslt来组合共享公共属性值的元素及其子元素。我已经设法使用xsl:keys执行此操作。

我坚持的部分是我还需要基本上重现原始树,除非具有特定属性的父元素与我正在生成的组合结构匹配,那么父元素应该没有子元素。您可以在提供的示例输入/输出中看到这一点。输出的第二部分中的<parent attribute="two"/>没有子节点,因为它最初包含所有子节点。

XML输入格式:

<root>
    <parent attribute="one">
        <child>one</child>
        <child>two</child>
        <child>three</child>
    </parent>
    <parent attribute="two">
        <child>one</child>
        <child>two</child>
        <child>three</child>
    </parent>
    <parent attribute="one">
        <child>two</child>
        <child>three</child>
        <child>four</child>
    </parent>
    <extra>
        <extra>
            <parent attribute="two">
                <child>three</child>
            </parent>
        </extra>
    </extra>
</root>

所需的输出格式:

<root>
    <first>
        <new attribute="one">
            <child>one</child>
            <child>two</child>
            <child>three</child>
            <child>four</child>
        </new>
        <new attribute="two">
            <child>one</child>
            <child>two</child>
            <child>three</child>
        </new>
    </first>
    <second>
        <parent attribute="one">
            <child>one</child>
            <child>two</child>
            <child>three</child>
        </parent>
        <parent attribute="two"/>
        <parent attribute="one">
            <child>two</child>
            <child>three</child>
            <child>four</child>
        </parent>
        <extra>
            <extra>
                <parent attribute="two">
                    <child>three</child>
                </parent>
            </extra>
        </extra>
    </second>
</root>

我目前的尝试涉及重复使用为输出的第一部分创建的密钥。我的目标是简单地将总唯一子计数与当前父子计数进行比较,以确定当前父节点是否包含所有组合子节点。我尝试了许多类似的方法来产生这个组合计数,如下所示,但我开始认为我正在解决这个问题,因为我要么返回每个子元素的计数或当前父元素子计数。

我没有考虑保留第二部分的原始结构的其余部分,因为这不是当时的紧迫问题。

任何建议都将不胜感激。如果我不善于搜索而其他人有类似的问题,请随时指示我。

XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="ParentKey" match="parent" use="@attribute"/>
  <xsl:key name="ChildKey" match="parent/child" use="../@attribute"/>

  <xsl:template match="@*|node()">
    <xsl:apply-templates select="@*|node()" />
  </xsl:template>

  <xsl:template match="/">
    <xsl:element name="first">
      <xsl:for-each select="//parent[count(. | key('ParentKey', @attribute)[1]) = 1]">
        <xsl:element name="new">
          <xsl:attribute name="attribute">
            <xsl:value-of select="@attribute"/>
          </xsl:attribute>
          <xsl:apply-templates select="child[count(. | key('ChildKey', @attribute)[1]) = 1]"/>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>

    <xsl:element name="second">
      <xsl:apply-templates select="//parent" mode="second"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="child">
    <xsl:element name="child">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="parent" mode="second">
    <xsl:element name="parent">
      <xsl:attribute name="attribute">
        <xsl:value-of select="@attribute"/>   
      </xsl:attribute>
      <!-- currently problematic line -->
      <xsl:variable name="combinedCount" select="count(//parent/child[count(. | key('ChildKey', @attribute)[1]) = 1])"/>
      <xsl:variable name="currentCount" select="count(child)"/>
      <xsl:if test="not($combinedCount = $currentCount)">
        <xsl:apply-templates select="child"/>
      </xsl:if>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:0)

您正在使用Muenchian Grouping,所以我假设您使用的是XSLT 1.0(与Muenchian Grouping一样,在XSLT 2.0中有更简单的方法进行分组)。

但您需要更改的主要内容是您对ChildKey的定义,因为您实际上正在为每个可能的父属性寻找不同的子元素,因此您需要像这样定义您的键:

<xsl:key name="ChildKey" match="child" use="concat(../@attribute, '|', .)"/>

然后,您的combined计数变量设置如下,以计算给定父属性的不同子元素:

 <xsl:variable name="combinedCount" select="count(key('ParentKey', @attribute)/child[count(. | key('ChildKey', concat(../@attribute, '|', .))[1]) = 1])"/>

试试这个XSLT。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="ParentKey" match="parent" use="@attribute"/>
  <xsl:key name="ChildKey" match="child" use="concat(../@attribute, '|', .)"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
        <first>
          <xsl:for-each select="//parent[count(. | key('ParentKey', @attribute)[1]) = 1]">
            <new attribute="{@attribute}">
              <xsl:apply-templates select="child[count(. | key('ChildKey', @attribute)[1]) = 1]"/>
            </new>
          </xsl:for-each>
        </first>
        <second>
          <xsl:apply-templates />
        </second>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="parent">
    <parent attribute="{@attribute}">
      <!-- currently problematic line -->
      <xsl:variable name="combinedCount" select="count(key('ParentKey', @attribute)/child[count(. | key('ChildKey', concat(../@attribute, '|', .))[1]) = 1])"/>
      <xsl:variable name="currentCount" select="count(child)"/>
      <xsl:if test="not($combinedCount = $currentCount)">
        <xsl:apply-templates select="child"/>
      </xsl:if>
    </parent>
  </xsl:template>
</xsl:stylesheet>