XSLT 分组和合并节点

XSLT Grouping and merging nodes

是否可以对节点进行分组并与指定键合并?我的一些 xml 文件在两个子节点中包含有关一个人的信息,我想查找节点是否与同一个人有关,然后将其合并为一个节点

输入 xml 如下所示:

<db>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>304888</number>
        <details>
            <city>Westfield</city>
            <zip-code>07090</zip-code>
            <address>23 Victoria Street</address>
            <phone>123456789</phone>
            <fax/>
        </details>
    </next>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>304888</number>
        <details>
            <city>Westfield</city>
            <zip-code>07090</zip-code>
            <address>23 Victoria Street</address>
            <phone>223344123</phone>
            <fax>993456789</fax>
        </details>
    </next>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>113190</number>
        <details>
            <city>Richmond</city>
            <zip-code>3121</zip-code>
            <address>18 Seasame Street</address>
            <phone>123456222</phone>
            <fax/>
        </details>
    </next>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>113190</number>
        <details>
            <city>Richmond</city>
            <zip-code>3133</zip-code>
            <address>23 Baker Street</address>
            <phone>113344123</phone>
            <fax>133456789</fax>
        </details>
    </next>
</db>

'number'是一个人的ID,可以有更多的人同名同姓但'number'值不同。此外,同一个人可能不止一次(具有相同的 'name'、'surename' 和 'number' 值)但具有不同的详细信息。如果 'city'、'zip-code' 和 'address' 的组合相同,但 'phone' or/and 'fax' 不同,那么我想合并它。 我希望它看起来像这样:

<db>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>304888</number>
        <details>
            <city>Westfield</city>
            <zip-code>07090</zip-code>
            <address>23 Victoria Street</address>
            <phone>123456789</phone>
            <phone>223344123</phone>
            <fax>993456789</fax>
        </details>
    </next>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>113190</number>
        <details>
            <city>Richmond</city>
            <zip-code>3121</zip-code>
            <address>18 Seasame Street</address>
            <phone>123456222</phone>
            <fax/>
        </details>
    </next>
    <next>
        <name>John</name>
        <surname>Smith</surname>
        <number>113190</number>
        <details>
            <city>Richmond</city>
            <zip-code>3133</zip-code>
            <address>23 Baker Street</address>
            <phone>113344123</phone>
            <fax>133456789</fax>
        </details>
    </next>
</db>

我尝试过 muenchian 分组,但我不知道如何使用 phone 和传真节点。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="index" match="next" use="number" />
  <xsl:key name="details-key" match="details" use="city,'_',zip-code,'_',address" />

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="db">
    <xsl:copy>
      <xsl:for-each select="//next[generate-id() = generate-id(key('index', number)[1])]">
        <xsl:element name="name">
          <xsl:value-of select="name"/>
        </xsl:element>
        <xsl:element name="surname">
          <xsl:value-of select="surname"/>
        </xsl:element>
        <xsl:element name="number">
          <xsl:value-of select="number"/>
        </xsl:element>
        <next>
          <xsl:for-each select="//details[generate-id() = generate-id(key('details-key', concat(city,'_',zip-code,'_',address))[1])]">
            <xsl:element name="city">
              <xsl:value-of select="city"/>
            </xsl:element>
            <xsl:element name="zip-code">
              <xsl:value-of select="zip-code"/>
            </xsl:element>
            <xsl:element name="address">
              <xsl:value-of select="address"/>
            </xsl:element>
          </xsl:for-each>
        </next>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

首先,您的样式表会产生错误,因为您在以下中使用的表达式:

<xsl:key name="details-key" match="details" use="city,'_',zip-code,'_',address" />

不是 XPath 1.0 中的有效表达式。

如果您希望通过 cityzip-codeaddress 的组合值对 details 元素进行分组,您应该将键定义为:

<xsl:key name="details-key" match="details" use="concat(city, '_', zip-code, '_', address)" />

现在,如果我按照您的预期输出而不是您的描述,您实际上想要为 number 和地址的每个唯一组合创建一个组。所以类似的东西:

XSLT 1.0

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

<xsl:key name="k1" match="next" use="concat(number, '_', details/city,'_', details/zip-code,'_', details/address)" />
    
<xsl:template match="db">
    <xsl:copy>
        <xsl:for-each select="next[generate-id() = generate-id(key('k1', concat(number, '_', details/city,'_', details/zip-code,'_', details/address))[1])]">
            <next>
                <xsl:copy-of select="name | surname | number"/>
                <details>
                    <xsl:copy-of select="city | zip-code | address"/>
                    <xsl:variable name="group-details" select="key('k1', concat(number, '_', details/city,'_', details/zip-code,'_', details/address))/details" />
                    <xsl:copy-of select="$group-details/phone[text()] | $group-details/fax[text()]"/>
                </details>
            </next>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
  
</xsl:stylesheet>