Home > Software design >  Move sibling node into current node with XSL template
Move sibling node into current node with XSL template

Time:02-06

I'm new to XSL Templates and am attempting what I thought would be a simple transformation, but I am not getting very far. I want to transform this XML:

<Books>
    <Name>
        <p>Romeo and Juliet</p>
    </Name>
    <Text>
        <p>Two houses...</p>
    </Text>
    <Name>
        <p>Hamlet</p>
    </Name>
    <Text>
        <p>Who's there...</p>
    </Text>
</Books>

to this:

<Books>
    <Book>
        <Title>Romeo and Juliet</Title>
        <Content>
            <p>Two houses...</p>
        </Content>
    </Book>
    <Book>
        <Title>Hamlet</Title>
        <Content>
            <p>Who's there...</p>
        </Content>
    </Book>
</Books>

Note the following changes:

  1. Group the Name element and its proceeding Text element into a Book element and rename them to Title and Content.
  2. Strip the p tag from the Name, but not from the Text.

Here is my latest sorry attempt, which is not working:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="yes"/>

    <xsl:template match="Name">
        <Book>
            <Title>
                <xsl:copy-of select="."/>
            </Title>
            <Content>
                <xsl:copy select="following-sibling::Text[1]"/>
            </Content>
        </Book>
    </xsl:template>


    <xsl:template name="gettitle" match="p">
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Any help would be appreciated!

CodePudding user response:

If they always come in pairs, why don't you do simply:

XSLT 1.0

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

<xsl:template match="/Books">
    <xsl:copy>
        <xsl:for-each select="Name">
            <Book>
                <Title>
                    <xsl:value-of select="p"/>
                </Title>
                <Content>
                    <xsl:copy-of select="following-sibling::Text[1]/p"/>
                </Content>
            </Book>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

In XSLT 2.0 you could do:

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

<xsl:template match="/Books">
    <xsl:copy>
        <xsl:for-each-group select="*" group-starting-with="Name">
            <Book>
                <Title>
                    <xsl:value-of select="p"/>
                </Title>
                <Content>
                    <xsl:copy-of select="current-group()[self::Text]/p"/>
                </Content>
            </Book>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

This would work with any number of Text elements following a Name, creating a p element in Content for each one.

  •  Tags:  
  • Related