raberana
raberana

Reputation: 11816

XSLT add child node and move children node outside

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

Answers (1)

Mathias M&#252;ller
Mathias M&#252;ller

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

Related Questions