XSLT merge multiple nodes under same parent - xslt

Input XML is
<Operations>
<ID>10</ID>
<UserArea>
<AdditionalPhantomInformation>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241289</SubComponent>
<Position>1</Position>
</PhantomItem>
</AdditionalPhantomInformation>
</UserArea>
<ConsumedItem>
<LineNumber>3</LineNumber>
<ParentItem>40241288</ParentItem>
</ConsumedItem>
<UserArea>
<AdditionalPhantomInformation>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241302</SubComponent>
<Position>5</Position>
</PhantomItem>
</AdditionalPhantomInformation>
</UserArea>
</Operations>
And my expected output is
<Operations>
<ID>10</ID>
<UserArea>
<AdditionalPhantomInformation>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241289</SubComponent>
<Position>1</Position>
</PhantomItem>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241302</SubComponent>
<Position>5</Position>
</PhantomItem>
</AdditionalPhantomInformation>
</UserArea>
<ConsumedItem>
<LineNumber>3</LineNumber>
<ParentItem>40241288</ParentItem>
</ConsumedItem>
</Operations>
I have searched various sources and tried, but i'm unable to get the xslt right. I don't know how to use xsl:for-each-group. Please help. I'm using XSLT 2.0.

You don't necessarily need xsl:for-each-group here, as all you are doing is combining specific nodes into one.
Start off with the identity template...
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Or, if you could use XSLT 3.0....
<xsl:mode on-no-match="shallow-copy"/>
Then have a template matching the first UserArea which does the combining...
<xsl:template match="UserArea[1]">
<xsl:copy>
<xsl:apply-templates select="node()|following-sibling::UserArea/node()" />
</xsl:copy>
</xsl:template>
You would then just need another template to ensure the other UserArea elements are not output in their original position.
<xsl:template match="UserArea" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="UserArea[1]">
<xsl:copy>
<xsl:apply-templates select="node()|following-sibling::UserArea/node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="UserArea" />
</xsl:stylesheet>
Note, if you did want to use xsl:for-each-group you would do it this way
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Operations">
<xsl:copy>
<xsl:for-each-group select="*" group-by="name()">
<xsl:choose>
<xsl:when test="current-grouping-key() = 'UserArea'">
<xsl:copy>
<xsl:apply-templates select="current-group()/node()" />
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This has the advantage of being easily extended if you had nodes other than UserArea you wished to combine.

Related

xslt, Apply Child node counter to all attribute in child nodes

For XML Transformation, i have to transform XML in such a way in adds counter value to all child node attribute Below is my xml sample
<hs>
<hscode>
<hsdetail>
<Name>Shirt</Name>
<ItemPrice>30</ItemPrice>
</hsdetail>
<hsdetail>
<Name>Shirt</Name>
<ItemPrice>30</ItemPrice>
</hsdetail>
</hscode>
</hs>
Using Xslt i want to apply counter on each child nodes of , id there are multiple hsdetails, each attribute in this node will use counter, and so on, Theconverted xml looks like below
<hs>
<hscode>
<hsdetail>
<Name1>Shirt</Name1>
<ItemPrice1>30</ItemPrice1>
</hsdetail>
<hsdetail>
<Name2>Shirt</Name2>
<ItemPrice2>30</ItemPrice2>
</hsdetail>
</hscode>
</hs>
I am using xsl but does not seems to be working when applying transformation
Any help on this? The xsl is as follows:
<xsl:stylesheet xmlns:xsl="w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="hsdetail/*">
<xsl:element
name="{name()}{count(preceding-sibling::*[name() = name(current())]) + 1}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template> </xsl:stylesheet>
How about:
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="hsdetail">
<xsl:variable name="i" select="position()" />
<xsl:copy>
<xsl:for-each select="*">
<xsl:element name="{name()}{$i}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT convert xml block under a specific node to xml-escaped content of that node

Any ideas of how the following problem can be solved would be highly appreciated.
INPUT:
<p>
<div>
Original<br/>This is the original <b>acid</b>, a hydroxy monocarboxylic <span class="hl1">acid</span>.
</div>
</p>
Desired Output:
<p>
<div>
Original<br/>This is the original <b>acid</b>, a hydroxy monocarboxylic <span class="hl1">acid</span>.
</div>
</p>
Attempt 1:
`<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" indent="no" encoding="UTF-8"/>
<!--The identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:value-of select="/" disable-output-escaping="no"/>
</xsl:copy>
</xsl:template>
`
Attempt2:
as an alternative, I thought of placing the child elements' content into a CDATA wrapper.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" indent="no" encoding="UTF-8"/>
<!--The identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:value-of select="/" />
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>
But that does not give me what I want.
Anyone with a better idea? I'm using XSLT 2.0
Here is a suggestion using XSLT 3.0 serialize() as supported by Saxon 9.6 HE, PE and EE:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:apply-templates mode="serialize"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" mode="serialize">
<xsl:variable name="ser-params">
<output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:value-of select="serialize(., $ser-params/*)"/>
</xsl:template>
</xsl:stylesheet>
With older version of Saxon 9 you could use the extension function serialize, as shown in http://xsltransform.net/pPqsHTx:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output name="inline" omit-xml-declaration="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:apply-templates mode="serialize"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" mode="serialize">
<xsl:value-of xmlns:saxon="http://saxon.sf.net/" select="saxon:serialize(., 'inline')"/>
</xsl:template>
</xsl:stylesheet>
Your second attempt should work if you change your xsl:value-of to an xsl:copy-of and tweak the select:
<xsl:template match="div">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:copy-of select="node()" />
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>

XSLT 2.0 - passing a node set as a parameter doesn't seem to work

I have a document that I need to transform such that most elements are copied as-is, with some exceptions: for certain specified nodes, child elements need to be appended, and some of these child elements need to reference back to specific elements in the source document. A separate "model/crosswalk" xml file contains the elements to add. The elements in the crosswalk that need to refer back to the source document have "source" attributes that point them to specific elements. Of course, my actual source docs (& the actual crosswalk) are more complex and varied than these examples.
Here is a source document example:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<album>
<artist>Frank Sinatra</artist>
<title>Greatest Hits</title>
</album>
<album>
<artist>Miles Davis</artist>
<title>Kind Of Blue</title>
</album>
<movie>
<title>ET</title>
<director>Steven Spielberg</director>
</movie>
<movie>
<title>Blues Brothers</title>
<director>John Landis</director>
</movie>
</root>
Here is the "crosswalk" (crswlk.xml):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<album>
<artist-info>
<artist2 source="artist"/>
</artist-info>
</album>
<movie>
<director-info>
<director2 source="director"/>
</director-info>
</movie>
</root>
And here is the desired output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<album>
<artist>Frank Sinatra</artist>
<title>Greatest Hits</title>
<artist-info>
<artist2>Frank Sinatra</artist2>
</artist-info>
</album>
<album>
<artist>Miles Davis</artist>
<title>Kind Of Blue</title>
<artist-info>
<artist2>Miles Davis</artist2>
</artist-info>
</album>
<movie>
<title>ET</title>
<director>Steven Spielberg</director>
<director-info>
<director2>Steven Spielberg</director2>
</director-info>
</movie>
<movie>
<title>Blues Brothers</title>
<director>John Landis</director>
<director-info>
<director2>John Landis</director2>
</director-info>
</movie>
</root>
Here's my xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.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="*"/>
<xsl:variable name="crosswalk" select="document('crswlk.xml')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/album|movie">
<xsl:variable name="theNode" select="."/>
<xsl:variable name="nodeName" select="name()"/>
<xsl:element name="{$nodeName}">
<xsl:apply-templates select="#* | node()"/>
<xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
<xsl:with-param name="curNode" select="$theNode"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="*[#source]">
<xsl:param name="curNode" />
<xsl:variable name="sourceNodeName" select="#source"/>
<xsl:element name="{name()}">
<xsl:value-of select="$curNode//*[name()=$sourceNodeName]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This stops processing once that last template tries to access $curNode for the first time. When I run the xslt in Eclipse (Xalan 2.7.1) it throws this error: " java.lang.ClassCastException: org.apache.xpath.objects.XString cannot be cast to org.apache.xpath.objects.XNodeSet".
If I pass a similar nodeset as a parameter to a template that matches nodes from the source document, it works as expected - the nodeset is accessible. However, passing the nodeset to the last template above doesn't work. Is it because the template matches nodes from the external document? I sure don't know. Any help much appreciated, it took me a while just to get to this point. Thanks!
Looks like you need to change 2 things:
add tunnel="yes" to xsl:with-param and xsl:param
remove the apostrophes from '$sourceNodeName' in the predicate of the xsl:value-of
Updated XSLT:
<xsl:stylesheet version="2.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="*"/>
<xsl:variable name="crosswalk" select="document('crswlk.xml')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/album|movie">
<xsl:variable name="theNode" select="."/>
<xsl:variable name="nodeName" select="name()"/>
<xsl:element name="{$nodeName}">
<xsl:apply-templates select="#* | node()"/>
<xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
<xsl:with-param name="curNode" select="$theNode" tunnel="yes"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="*[#source]">
<xsl:param name="curNode" tunnel="yes"/>
<xsl:variable name="sourceNodeName" select="#source"/>
<xsl:element name="{name()}">
<xsl:value-of select="$curNode//*[name()=$sourceNodeName]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Also, you can remove a few of those extra xsl:variables...
<xsl:stylesheet version="2.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="*"/>
<xsl:variable name="crosswalk" select="document('crswlk.xml')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="album|movie">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:apply-templates select="$crosswalk/*/*[name()=current()/name()]/*">
<xsl:with-param name="curNode" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#source]">
<xsl:param name="curNode" tunnel="yes"/>
<xsl:copy>
<xsl:value-of select="$curNode//*[name()=current()/#source]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you are using XSLT 2.0, then Daniel Haley's answer with tunnelling is surely the way to go. If, however, you are actually using Xalan, and therefore only XSLT 1.0, you need to take a different approach.
The problems start on this line:
<xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
This will select either artist-info or director-info in your cross walk document, but you have no specific template matching these, so the generic identity template you are using will match them
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
But this does not take any parameter, not does it pass any parameters on. Therefore, when your last template <xsl:template match="*[#source]"> is matched, the curNode will be empty (an empty string), which is not a node-set, and that saddens Xalan.
So, to solve this in XSLT1.0, just add the parameter to the identity template, and pass it on:
<xsl:template match="node()|#*">
<xsl:param name="curNode" />
<xsl:copy>
<xsl:apply-templates select="#* | node()">
<xsl:with-param name="curNode" select="$curNode" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
If the template is ever matched when there is no parameter being passed, it will just pass on an empty parameter without any issue.
Here is the full XSLT (also with the correction of apostrophes being removed from the xsl:value-of, as mentioned in Daniel's answer).
<xsl:stylesheet version="2.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="*"/>
<xsl:variable name="crosswalk" select="document('crswlk.xml')"/>
<xsl:template match="node()|#*">
<xsl:param name="curNode" />
<xsl:copy>
<xsl:apply-templates select="#* | node()">
<xsl:with-param name="curNode" select="$curNode" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="*/album|movie">
<xsl:variable name="theNode" select="."/>
<xsl:variable name="nodeName" select="name()"/>
<xsl:element name="{$nodeName}">
<xsl:apply-templates select="#* | node()"/>
<xsl:apply-templates select="$crosswalk//*[name()=$nodeName]/*">
<xsl:with-param name="curNode" select="$theNode"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="*[#source]">
<xsl:param name="curNode" />
<xsl:variable name="sourceNodeName" select="#source"/>
<xsl:element name="{name()}">
<xsl:value-of select="$curNode//*[name()=$sourceNodeName]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When applied to your XML documents, the following is output
<root>
<album>
<artist>Frank Sinatra</artist>
<title>Greatest Hits</title>
<artist-info>
<artist2>Frank Sinatra</artist2>
</artist-info>
</album>
<album>
<artist>Miles Davis</artist>
<title>Kind Of Blue</title>
<artist-info>
<artist2>Miles Davis</artist2>
</artist-info>
</album>
<movie>
<title>ET</title>
<director>Steven Spielberg</director>
<director-info>
<director2>Steven Spielberg</director2>
</director-info>
</movie>
<movie>
<title>Blues Brothers</title>
<director>John Landis</director>
<director-info>
<director2>John Landis</director2>
</director-info>
</movie>
</root>

How to remove the rootnodes using XSLT?

Input file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://xyz.com/separate">
<ns0:root1>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns3:Detail>
</ns0:root1>
</ns0:root>
Output file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Detail xmlns:ns0="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns0:Detail>
Question: I have to remove the root1 and root nodes and need to do small
changes in Detail node. How to write a xslt code to achieve this?
This...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate"
xmlns:ns3="http://POProject/Details">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns3:Detail">
<xsl:apply-templates select="." mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...will yield this...
<?xml version="1.0" encoding="utf-8"?>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID />
<ItemDescription />
</DetailLines>
</ns3:Detail>
I'm not sure it is possible to control the prefix. The XDM data model does not consider it to be significant information.
UDPATE
To get the prefix rename, I thought you would have to go to an XML 1.1 supporting XSLT processor (allowing prefix undefine), but I found a way to do it with XML 1.0 . Try this ...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/" xmlns:ns3="http://POProject/Details">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns0:Detail" xmlns:ns0="http://POProject/Details">
<ns0:Detail xmlns:ns0="http://POProject/Details">
<xsl:apply-templates select="*" mode="copy-sans-namespace" />
</ns0:Detail>
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Transform Exception. Adding Attribute after Sub-element

I have this XML:
<root>
<tab name="Detail">
<section name="mysection">
<items level="1">
<Idx_name>9</Idx_name>
<Type>mytype</Type>
<item name="myname">
<Grams um="(g)">9,0</Grams>
<Pre-infusion>Max</Pre-infusion>
</item>
<Std._Mode>On</Std._Mode>
<Price>100</Price>
</items>
</section>
</tab>
</root>
and this XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items/*">
<xsl:choose>
<xsl:when test="not(name()='item')">
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Now, what I want is:
<root>
<tab name="Detail">
<section name="mysection">
<items level="1" Idx_name="9" Type="mytype" Std._Mode="On" Price="100">
<item name="myname">9,0Max</item>
</items>
</section>
</tab>
</root>
I obtain the error: "An attribute cannot be added after a child"
Unluckily, I can not change the order of elements in the node items of my original XML
How can I do it ?
Thanks
Ivan
Make sure you process the elements first you want to transform into attributes e.g. with XSLT 2.0 where you can process sequences which have a order you can simply do
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items">
<xsl:copy>
<xsl:apply-templates select="#*, * except item, item"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items/*[not(self::item)]">
<xsl:attribute name="{name()}" select="."/>
</xsl:template>
<xsl:template match="items/item">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With XSLT 1.0 you would need to spell out several apply-templates in the order you want.