Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT: Copy attribute into a new element in the child

I want to copy the id attribute on a specific element (parent in this case) into a new element within the child of parent.

My XML is as follows:

<wrapper>
    <parent id="1">
         <child>
            content
         </child>
    </parent>
</wrapper>

I need the output to be like this:

<wrapper>
    <parent>
        <child>
            <parentid>1</parentid> 
            content
        </child>
    </parent>
</wrapper>

From the examples below this one does what i want, except that it puts the new element in parent, whilst i want in the child:

<xsl:template match="parent[@id]">
    <parent>
        <xsl:apply-templates select="@*[name() != 'id']" />
            <parentid>
                <xsl:value-of select="@id" />
            </parentid>
        <xsl:apply-templates select="node()"/>
    </parent>
</xsl:template>

I've been messing around with the above but i still can't get it the way i want. Thanks in advance.

like image 798
Corrr Avatar asked Mar 23 '23 07:03

Corrr


1 Answers

You don't say if you want this for all attributes or just the id attribute, or whether it is just the attributes on one element, or all elements.

Let's assume you want to do it for the id attribute on all elements, but leave other attributes untouched. Then you would have a template to match any such element with an id attribute like so:

<xsl:template match="*[@id]">

And within this you can create a new element based on the current element name, like so:

     <xsl:element name="{name()}id">
        <xsl:value-of select="@id" />
     </xsl:element>

Try this XSLT for a start

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="*[@id]">
      <xsl:copy>
         <xsl:apply-templates select="@*[name() != 'id']" />
         <xsl:element name="{name()}id">
            <xsl:value-of select="@id" />
         </xsl:element>
         <xsl:apply-templates select="node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

Note the use of Attribute Value Templates (the curly braces) in dynamically creating the new element name. Also note you have to output the other attributes before creating the new child element as it is considered an error to output an attribute for an element after a child element has been created for it.

Of course, if you wanted only the id attribute on a specific element, you can simplify the second template to this

<xsl:template match="parent[@id]">
   <parent>
      <xsl:apply-templates select="@*[name() != 'id']" />
      <parentid>
         <xsl:value-of select="@id" />
      </parentid>
      <xsl:apply-templates select="node()"/>
   </parent>
</xsl:template>

But if you wanted to transform all attributes into elements, you could do this in a fully generic way, like so

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="@*">
      <xsl:element name="{name(..)}{name()}">
         <xsl:value-of select="." />
      </xsl:element>
   </xsl:template>
</xsl:stylesheet>

EDIT:

If the requirement is actually to add the id attribute as a child of the child element, a little bit of tweaking is needed. Firstly, you need to a template to stop the id attribute on the parent being output at the point the element is copied

<xsl:template match="parent/@id" />

Then you need a template to match the child of the parent element

<xsl:template match="parent[@id]/*[1]">

(In this case, I am assuming it will always be the first child element. If you wanted a specific child element, just use the name here)

Within this template you can then just create your new element using the id attribute from the parent.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="parent/@id" />

   <xsl:template match="parent[@id]/*[1]">
      <xsl:copy>
         <xsl:apply-templates select="@*" />
         <parentid>
           <xsl:value-of select="../@id" />
         </parentid>
         <xsl:apply-templates select="node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>
like image 95
Tim C Avatar answered Apr 08 '23 13:04

Tim C