How to move an attribute and its value from one element to another

1.1k views Asked by At

I have the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<Orders>
<Order Weight="1.00">
<Items>
<Item ItemLength="5.00" ItemQty="1">                    
<ItemCharge Description="Chair" Amount="5.50"></ItemCharge>
</Item>
</Items>
</Order>

<Order Weight="2.50">
<Items>
<Item Length="5.00" ItemQty="1">                    
<ItemCharge Description="Chair" Amount="8.50"></ItemCharge>
</Item>
</Items>
</Order>
</Orders>

I need the attributes and values in the "Order" element (e.g. Weight="1.00") moved to the "Item" element. The number of "Order" elements and the value in the "Weight" attribute will vary from time to time. The desired result should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<Orders>
<Order>
<Items>
<Item ItemLength="5.00" ItemQty="1" Weight="1.00">                    
<ItemCharge Description="Chair" Amount="5.50"></ItemCharge>
</Item>
</Items>
</Order>

<Order>
<Items>
<Item Length="5.00" ItemQty="1" Weight="2.50">
<ItemCharge Description="Chair" Amount="8.50"></ItemCharge>
</Item>
</Items>
</Order>
</Orders>

I currently have this XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="@*[not(name() = 'Weight')] | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template> 
</xsl:stylesheet>

I greatly appreciate your help!!!

1

There are 1 answers

1
Tim C On BEST ANSWER

You need to think of this as two separate changes, rather than a single "move"

1) Remove the Weight attribute for the Order element. Or rather, do not copy the Weight attribute to the output

 <xsl:template match="@Weight" />

2) Add a Weight attribute to the Item element when copying it to the output

<xsl:template match="Item">
   <Item Weight="{../../@Weight}">
      <xsl:apply-templates select="@* | node()"/>
   </Item>
</xsl:template>

Note the use of "Attribute Value Templates" in creating the new attribute.

Also note you should avoid changing the identity template in such transformations. The two templates above, because they use specific names, get a higher priority than the identity template.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
   <xsl:output method="xml" indent="yes"/>

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

   <xsl:template match="@Weight" />

   <xsl:template match="Item">
      <Item Weight="{../../@Weight}">
         <xsl:apply-templates select="@* | node()"/>
      </Item>
   </xsl:template>
</xsl:stylesheet>