Search code examples
xmlxsltnodesword-wrap

Wrap nodes in new parent node/element


I'm trying to wrap <code-text> in a div element.

Sample code:

<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />

<p>...</p>
<p>...</p>
<code-text ATTRIBUTE="JSON">...</code-text>
<code-text ATTRIBUTE="SOAP">...</code-text>
<p>...</p>
<code-text ATTRIBUTE="JSON">...</code-text>
<code-text ATTRIBUTE="SOAP">...</code-text>
<code-text ATTRIBUTE="JSON">...</code-text>
<code-text ATTRIBUTE="SOAP">...</code-text>
<p>...</p>
<p>...</p>
<code-text ATTRIBUTE="JSON">...</code-text>
<code-text ATTRIBUTE="SOAP">...</code-text>

I've seen a couple of suggestions on this site already, but when I reproduce their solutions, it hasn't worked 100%. The closest I got, was from How can I wrap a group of adjacent elements using XSLT?, but for some reason, it keeps adding the paragraph after as well.

So this is basically where I'm at:

<xsl:template match="code-text[@ATTRIBUTE]">
    <xsl:element name="div">
        <xsl:attribute name="class">
            <xsl:text>codes</xsl:text>
        </xsl:attribute>
        <xsl:call-template name="code-text" />
    </xsl:element>
    <xsl:apply-templates select="following-sibling::node()[not(self::code-text[@ATTRIBUTE])][1]" />
</xsl:template>
<xsl:template match="code-text[@ATTRIBUTE][preceding-sibling::node()[1]/self::code-text[@ATTRIBUTE]]" name="code-text">
    <xsl:text>Code goes here: </xsl:text><xsl:value-of select="translate(@ATTRIBUTE, $uppercase, $smallcase)" /><xsl:element name="br" />
    <xsl:apply-templates select="following-sibling::node()[1]/self::code-text[@ATTRIBUTE]" />
</xsl:template>

But this outputs:

<div class="codes">
Code goes here: JSON<br>
Code goes here: SOAP
</div>
<p>...</p>
Code goes here :SOAP
<p>...</p>

So the last SOAP and the Paragraph after it is repeated.

I want:

<div class="codes">
Code goes here: JSON<br>
Code goes here: SOAP
</div>
<p>...</p>
etc...etc...etc

Update To clarify about repeating json/soap/json/soap:

<code-text ATTRIBUTE="JSON">...</code-text>
<code-text ATTRIBUTE="SOAP">...</code-text>
<code-text ATTRIBUTE="JSON">...</code-text>
<code-text ATTRIBUTE="SOAP">...</code-text>

I want to become:

<div class="codes">
Code goes here: JSON<br>
Code goes here: SOAP
</div>
<div class="codes">
Code goes here: JSON<br>
Code goes here: SOAP
</div>

Instead of:

<div class="codes">
Code goes here: JSON<br>
Code goes here: SOAP<br>
Code goes here: JSON<br>
Code goes here: SOAP
</div>

Solution

  • To create a div for each pair of <code-text ATTRIBUTE="JSON"/><code-text ATTRIBUTE="SOAP"/> you could do simply:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="/root">
        <body>
            <xsl:apply-templates select="p | code-text[@ATTRIBUTE='JSON']"/>
        </body>
    </xsl:template>
    
    <xsl:template match="code-text">
         <div class="codes">
            <xsl:value-of select="." />
            <br/>
            <xsl:value-of select="following-sibling::code-text[1]" />
         </div>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Applied to the following well-formed example input:

    XML

    <root>
        <p>a</p>
        <p>b</p>
        <code-text ATTRIBUTE="JSON">c1</code-text>
        <code-text ATTRIBUTE="SOAP">c2</code-text>
        <p>d</p>
        <code-text ATTRIBUTE="JSON">e1</code-text>
        <code-text ATTRIBUTE="SOAP">e2</code-text>
        <code-text ATTRIBUTE="JSON">f1</code-text>
        <code-text ATTRIBUTE="SOAP">f2</code-text>
        <p>g</p>
        <p>h</p>
        <code-text ATTRIBUTE="JSON">i1</code-text>
        <code-text ATTRIBUTE="SOAP">i2</code-text>
    </root>
    

    the result will be:

    <body>
       <p>a</p>
       <p>b</p>
       <div class="codes">c1<br/>c2</div>
       <p>d</p>
       <div class="codes">e1<br/>e2</div>
       <div class="codes">f1<br/>f2</div>
       <p>g</p>
       <p>h</p>
       <div class="codes">i1<br/>i2</div>
    </body>