Reputation: 11816
I have an input XML below:
<Dogs>
<Group Name="Cuties">
<Dog Breed="Beagle">
<Detail1>...</Detail1>
</Dog>
<Dog Breed="French Bulldog">
<Detail1>...</Detail1>
</Dog>
</Group>
<Group Name="Lovable">
<Dog Breed="Labrador">
<Detail1>...</Detail1>
</Dog>
<Dog Breed="Terrier">
<Detail1>...</Detail1>
</Dog>
</Group>
<Group Name="Bad">
<Dog Breed="S">
<Detail1>...</Detail1>
</Dog>
<Dog Breed="D">
<Detail1>...</Detail1>
</Dog>
</Group>
</Dogs>
My desired output is to add another child node inside <Dog>
and move them out of <Group>
. The new child node should be the value of the Name
attribute of their group. This should only affect Cuties
and Lovable
groups
<Dogs>
<Dog Breed="Beagle">
<Detail1>...</Detail1>
<Detail2>Cuties</Detail2>
</Dog>
<Dog Breed="French Bulldog">
<Detail1>...</Detail1>
<Detail2>Cuties</Detail2>
</Dog>
<Dog Breed="Labrador">
<Detail1>...</Detail1>
<Detail2>Lovable</Detail2>
</Dog>
<Dog Breed="Terrier">
<Detail1>...</Detail1>
<Detail2>Lovable</Detail2>
</Dog>
<Group Name="Bad">
<Dog Breed="S">
<Detail1>...</Detail1>
</Dog>
<Dog Breed="D">
<Detail1>...</Detail1>
</Dog>
</Group>
</Dogs>
I'm just starting to learn XSLT and it would be a great help to figure this out
Upvotes: 0
Views: 537
Reputation: 22617
Start with an identity template, then add a template that matches the groups of dogs that should be omitted in the output:
<xsl:template match="Group[@Name = 'Cuties' or @Name = 'Lovable']">
and another that gives special treatment to the Dog
elements that are in those groups:
<xsl:template match="Group[@Name = 'Cuties' or @Name = 'Lovable']/Dog">
namely, add a Detail2
element as their last child element.
XSLT Stylesheet
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="no" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="Group[@Name = 'Cuties' or @Name = 'Lovable']">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="Group[@Name = 'Cuties' or @Name = 'Lovable']/Dog">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<Detail2>
<xsl:value-of select="../@Name"/>
</Detail2>
</xsl:copy>
</xsl:template>
<!--Identity template-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
If you are using XSLT 2.0, Martin Honnen has suggested you could shorten the first template to
<xsl:template match="Group[@Name = ('Cuties','Lovable')]">
The predicate will be true if the value of @Name
is the same as one of the strings in the sequence.
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<Dogs>
<Dog Breed="Beagle">
<Detail1>...</Detail1>
<Detail2>Cuties</Detail2>
</Dog>
<Dog Breed="French Bulldog">
<Detail1>...</Detail1>
<Detail2>Cuties</Detail2>
</Dog>
<Dog Breed="Labrador">
<Detail1>...</Detail1>
<Detail2>Lovable</Detail2>
</Dog>
<Dog Breed="Terrier">
<Detail1>...</Detail1>
<Detail2>Lovable</Detail2>
</Dog>
<Group Name="Bad">
<Dog Breed="S">
<Detail1>...</Detail1>
</Dog>
<Dog Breed="D">
<Detail1>...</Detail1>
</Dog>
</Group>
</Dogs>
Try this solution online here.
EDIT
is the purpose of identity template, to copy all as is?
Yes, exactly. If the identy template is the only template in your stylesheet, then all of the input is copied unchanged.
Upvotes: 2