How can you convert
<person>
<personFirstName>FirstName</personFirstName>
<personLastName>LastName</personLastName>
<personAge>40</personAge>
</person>
to
<person>
<name>
<first>FirstName</first>
<last>LastName</last>
</name>
<age>40</age>
</person>
using XSLT, moreover, if the input XML is a collection of person nodes, like so:
<persons>
<person>
...
</person>
</persons>
A "push-style" solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="personFirstName">
<name>
<xsl:apply-templates mode="renameWrapped"
select=".|../personLastName"/>
</name>
</xsl:template>
<xsl:template match="personFirstName" mode="renameWrapped">
<first><xsl:apply-templates/></first>
</xsl:template>
<xsl:template match="personLastName" mode="renameWrapped">
<last><xsl:apply-templates/></last>
</xsl:template>
<xsl:template match="personAge">
<age><xsl:apply-templates/></age>
</xsl:template>
<xsl:template match="personLastName"/>
</xsl:stylesheet>
when applied on this XML document:
<persons>
<person>
<personFirstName>FirstName</personFirstName>
<personLastName>LastName</personLastName>
<personAge>40</personAge>
</person>
<person>
<personFirstName>FirstName2</personFirstName>
<personLastName>LastName2</personLastName>
<personAge>100</personAge>
</person>
</persons>
the wanted, correct result is produced:
<persons>
<person>
<name>
<first>FirstName</first>
<last>LastName</last>
</name>
<age>40</age>
</person>
<person>
<name>
<first>FirstName2</first>
<last>LastName2</last>
</name>
<age>100</age>
</person>
</persons>
Explanation:
Using and overriding the identity rule/template for wrapping and renaming of elements.
The elements to be wrapped are renamed in mode renameWrapped
.
The personAge
element is renamed in a non-moded template that overrides the identity rule for elements named personAge
.
It should be very easy. You can try to:
- match
person
then open name
, apply templates, close name
, open age
, get value from personAge
, close age
- match
personFirstName
, open first
, get value, close first
- same as
personFirstName
for personLastName
I think 3 templates wihtout loops should be enough. Try it!
The key is the identity transform and overriding it when needed.
Sample XML
<persons>
<person>
<personFirstName>FirstName</personFirstName>
<personLastName>LastName</personLastName>
<personAge>40</personAge>
</person>
<person>
<personFirstName>FirstName2</personFirstName>
<personLastName>LastName2</personLastName>
<personAge>100</personAge>
</person>
</persons>
Sample XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output 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="person">
<person>
<name>
<first><xsl:apply-templates select="personFirstName"/></first>
<last><xsl:apply-templates select="personLastName"/></last>
</name>
<age><xsl:apply-templates select="personAge"/></age>
</person>
</xsl:template>
<xsl:template match="personFirstName|personLastName|personAge">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
OUTPUT
<persons>
<person>
<name>
<first>FirstName</first>
<last>LastName</last>
</name>
<age>40</age>
</person>
<person>
<name>
<first>FirstName2</first>
<last>LastName2</last>
</name>
<age>100</age>
</person>
</persons>