简体   繁体   中英

XSLT 2.0 - how to merge sibling elements

How can I merge a specific element with adjacent siblings of the same type into a single element? I feel this should be simple but can't find an answer.

My code:

...
<nodeX>
  <a>words</a>
  <b>things</b>
  <g>hi</g>
  <g>there</g>
  <g>friend</g>
  <c>stuff</c>
  <g>yep</g>
</nodeX>
...

Desired output:

...
<nodeX>
  <a>words</a>
  <b>things</b>
  <g>hi there friend</g>
  <c>stuff</c>
  <g>yep</g>
</nodeX>
...

I'm working with an extremely complex and varied document with a deep hierarchy, so I can't work with many assumptions beyond the fact that these elements will show up in some context or another, and when they appear with an adjacent sibling, those siblings need to merge. Any help would be appreciated.

Update:

Using zx485's & Martin Honnen's advice, the following seems to work well to unwrap only a specific element:

<xsl:template match="nodeX">
    <xsl:copy>
        <xsl:copy-of select="@*" />
        <xsl:for-each-group select="*" group-adjacent="name()">
            <xsl:choose>
                <xsl:when test="name()='g'">
                    <xsl:copy>
                        <xsl:copy-of select="current-group()/@*" />
                        <xsl:value-of select="current-group()"/>
                    </xsl:copy>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

With XSLT-2.0 this is not difficult. It is an XSLT-2.0 Grouping question. In this case use xsl:for-each-group with the attribute group-adjacent .

The following template matches all nodeX elements and copies them including their attributes. It iterates over all children and groups adjacent elements by their name() . Then it copies the first element of the group and adds the attributes of all elements with the same name. Now the text() s of all elements in this group are joined together with a separator - here it's a space.

<xsl:template match="nodeX">
    <xsl:copy>
        <xsl:copy-of select="@*" />
        <xsl:for-each-group select="*" group-adjacent="name()">
            <xsl:copy>
                <xsl:copy-of select="current-group()/@*" />
                <xsl:value-of select="current-group()" separator=" " />
            </xsl:copy>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template> 

Output is:

<nodeX>
   <a>words</a>
   <b>things</b>
   <g>hi there friend</g>
   <c>stuff</c>
   <g>yep</g>
</nodeX>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM