I.Alam
I.Alam

Reputation: 23

moving an attribute to a child element xml using xsl

I have been trying to move an attribute from one element to another but instead it creates another child element.

I have the following

<wrapper>
 <Person ID="1">
  <Person InfoName="bob" Gender="male" />
  <Purchase Reference = "1" Item="book"/>
  <Purchase Reference = "2" Item="shoes"/>
 </Person>
 <Person ID="2">
  <Person InfoName="Jane" Gender="female"/>
  <Purchase Reference = "1" Item="pen"/>
  <Purchase Reference = "2" Item="hat"/>
 </Person>
</wrapper>

and I need to have the following (I'm not precious about keeping as all the records will have the ID):

<wrapper>
 <Person ID="1">
  <Person InfoName="bob" Gender="male" ID = "1"/>
  <Purchase Reference = "1" Item="book" ID="1"/>
  <Purchase Reference = "2" Item="shoes" ID="1"/>
 </Person>
 <Person ID="2">
  <Person InfoName="Jane" Gender="female" ID="2"/>
  <Purchase Reference = "1" Item="pen" ID="2"/>
  <Purchase Reference = "2" Item="hat" ID="2"/>
 </Person>
</wrapper>

I have tried several different XSLTs with varying degrees of success. The following creates a new child node.

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

   <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>

Upvotes: 2

Views: 247

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

This is the only solution so far, which places the ID attribute last -- as wanted:

<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="Person/*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="../@ID"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

When applied on the provided XML document:

<wrapper>
    <Person ID="1">
        <Person InfoName="bob" Gender="male" />
        <Purchase Reference = "1" Item="book"/>
        <Purchase Reference = "2" Item="shoes"/>
    </Person>
    <Person ID="2">
        <Person InfoName="Jane" Gender="female"/>
        <Purchase Reference = "1" Item="pen"/>
        <Purchase Reference = "2" Item="hat"/>
    </Person>
</wrapper>

The exact (not like the results from other solutions) wanted result is produced:

<wrapper>
   <Person ID="1">
      <Person InfoName="bob" Gender="male" ID="1"/>
      <Purchase Reference="1" Item="book" ID="1"/>
      <Purchase Reference="2" Item="shoes" ID="1"/>
   </Person>
   <Person ID="2">
      <Person InfoName="Jane" Gender="female" ID="2"/>
      <Purchase Reference="1" Item="pen" ID="2"/>
      <Purchase Reference="2" Item="hat" ID="2"/>
   </Person>
</wrapper>

Upvotes: 0

uL1
uL1

Reputation: 2167

(1) Take care of a valid xml. Attributes must be Key-Value-pair e.g. info="a" (value must be in "). XML is case-sensitiv.

(2) Your solution is much simpler:

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

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="wrapper/Person/*">
        <xsl:copy>
            <xsl:apply-templates select="@* | ../@ID"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Select your elements to copy into via wrapper/Person/* and grep the attribute ID of parent via ...

Upvotes: 1

michael.hor257k
michael.hor257k

Reputation: 117083

Try it this way:

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:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Person/*">
    <xsl:copy>
        <xsl:apply-templates select="@*|../@ID|node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Applied to the following well-formed input:

<Wrapper>
<Person ID ="1">
 <Person Name="bob" Gender="male" />
 <Purchase Reference = "1" Item="book"/>
 <Purchase Reference = "2" Item="shoes"/>
</Person>

<Person ID ="2">
 <Person Name="Jane" Gender="female"/>
 <Purchase Reference = "1" Item="pen"/>
 <Purchase Reference = "2" Item="hat"/>
</Person>
</Wrapper>

the result will be:

<?xml version="1.0" encoding="UTF-8"?>
<Wrapper>
   <Person ID="1">
      <Person ID="1" Name="bob" Gender="male"/>
      <Purchase ID="1" Reference="1" Item="book"/>
      <Purchase ID="1" Reference="2" Item="shoes"/>
   </Person>
   <Person ID="2">
      <Person ID="2" Name="Jane" Gender="female"/>
      <Purchase ID="2" Reference="1" Item="pen"/>
      <Purchase ID="2" Reference="2" Item="hat"/>
   </Person>
</Wrapper>

Upvotes: 0

Related Questions