XSLT: combine nodes with the same name recursively

Even though there are many questions with similar titles on SO, I couldn't find an answer to my specific question.

Suppose I have a tree xml

:

<input>
    <a>
        <b>
            <p1/>
        </b>
    </a>
    <a>
        <b>
            <p2/>
        </b>
    </a>
</input>

      

I want to have it like

<input>
    <a>
        <b>
            <p1/>
            <p2/>
        </b>
    </a>
</input>

      

The idea behind this transformation is to transform a tree, where a node can have multiple children of the same name, to a more "well-formed" tree, where each node can only have one child with the same name, (cf file systems).

I tried using the grouping function xslt-2

, but I couldn't get the recursion to work.

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

  <!-- this merges the children -->
  <xsl:template name="merge" match="*">

      <xsl:for-each-group select="child::*" group-by="local-name()">
          <xsl:variable name="x" select="current-grouping-key()"/>
          <xsl:element name="{$x}">
              <xsl:copy-of select="current-group()/@*"/>
              <xsl:apply-templates select="current-group()"/>
              <xsl:copy-of select="text()"/>
          </xsl:element>
      </xsl:for-each-group>
  </xsl:template>
</xsl:stylesheet>

      

I see that the problem is that I am applying the template separately for each node in current-group()

, but I do not see how I can first "join" this set and apply the en bloc template.

+3


source to share


1 answer


I think you can customize the grouped function, see http://xsltransform.net/bdxtqM/1 which does



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="mf">

  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:function name="mf:merge" as="node()*">
    <xsl:param name="elements" as="element()*"/>
    <xsl:for-each-group select="$elements" group-by="node-name(.)">
        <xsl:copy>
            <xsl:copy-of select="current-group()/@*"/>
            <xsl:sequence select="mf:merge(current-group()/*)"/>
        </xsl:copy>
    </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:sequence select="mf:merge(*)"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

      

0


source







All Articles