xslt move attributes in child as element to parent node - xslt

I want to move my child nodes attributes as element to parent.
for ex.
Change the below xml
<Parent>
<Children>
<Child key="Name">ABC</Child>
<Child key="Age">8</Child>
<Child key="Height">140</Child>
<Child key="Class">6</Child>
</Children>
</Parent>
to
<Parent>
<Name>ABC</Name>
<Age>8</Age>
<Height>140</Height>
<Class>6</Class>
</Parent>
Hope my question is clear..

<xsl:template match="Parent">
<xsl:copy>
<xsl:apply-templates select="Children/Child"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Children/Child[#key]">
<xsl:element name="{#key}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:output indent="yes"/>
should suffice.

This compete and short transformation:
<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="/*">
<Parent>
<xsl:apply-templates/>
</Parent>
</xsl:template>
<xsl:template match="Child">
<xsl:element name="{#key}"><xsl:apply-templates/></xsl:element>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Parent>
<Children>
<Child key="Name">ABC</Child>
<Child key="Age">8</Child>
<Child key="Height">140</Child>
<Child key="Class">6</Child>
</Children>
</Parent>
produces the wanted, correct result:
<Parent>
<Name>ABC</Name>
<Age>8</Age>
<Height>140</Height>
<Class>6</Class>
</Parent>

Related

How to group parent elements based on the child elements in XSLT

I am tring to group multiple elements based on the child element that has the same value.
Each Child1 that has the same value should be inside of a node called Group. See desired output below.
Here is a sample XML
`<?xml version="1.0" encoding="utf-8"?>
<Root>
<Parent>
<Child1>0123</Child1>
<Child2>KIK</Child2>
<Child3>YAM</Child3>
</Parent>
<Parent>
<Child1>0123</Child1>
<Child2>FIS</Child2>
<Child3>BOL</Child3>
</Parent>
<Parent>
<Child1>0123</Child1>
<Child2>TOC</Child2>
<Child3>INO</Child3>
</Parent>
<Parent>
<Child1>456</Child1>
<Child2>CHI</Child2>
<Child3>KEN</Child3>
</Parent>
<Parent>
<Child1>456</Child1>
<Child2>ALA</Child2>
<Child3>KING</Child3>
</Parent>
</Root>`
I want the output to be like the following:
`<?xml version="1.0" encoding="utf-8"?>
<Root>
<GROUP>
<Parent>
<Child1>0123</Child1>
<Child2>KIK</Child2>
<Child3>YAM</Child3>
</Parent>
<Parent>
<Child1>0123</Child1>
<Child2>FIS</Child2>
<Child3>BOL</Child3>
</Parent>
<Parent>
<Child1>0123</Child1>
<Child2>TOC</Child2>
<Child3>INO</Child3>
</Parent>
</GROUP>
<GROUP>
<Parent>
<Child1>456</Child1>
<Child2>CHI</Child2>
<Child3>KEN</Child3>
</Parent>
<Parent>
<Child1>456</Child1>
<Child2>ALA</Child2>
<Child3>KING</Child3>
</Parent>
</GROUP>
</Root>`
I was only able to copy the whole code and not able to group it based on the values on Child1.
Sharing my solution below:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Root">
<Root>
<xsl:for-each-group select="Parent" group-by="Child1">
<xsl:choose>
<xsl:when test="count(current-group()) gt 1">
<Group>
<xsl:apply-templates select="current-group()" />
</Group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</Root>
</xsl:template>
</xsl:stylesheet>

Using muenchian grouping to select distinct values in a subtree

I know how to do muenchian grouping but that normally is applied to a whole document (?).
Here I want to find distinct values in subtrees.
e.g.
<root>
<parent>
<child id="1"/>
<child id="1"/>
<child id="2"/>
</parent>
<parent>
<child id="2"/>
<child id="3"/>
<child id="3"/>
</parent>
</root>
I want to map to
<root>
<parent>
<child id="1"/>
<child id="2"/>
</parent>
<parent>
<child id="2"/>
<child id="3"/>
</parent>
</root>
If I try
<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:key name="id_to_id" match="/root/parent/child" use="#id"/>
<xsl:template match="/root">
<root>
<xsl:for-each select="parent">
<parent>
<xsl:for-each select="child[generate-id() = generate-id(key('id_to_id',#id)[1])]">
<child id="{#id}"/>
</xsl:for-each>
</parent>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
then it does the key over the whole document and I get
<root>
<parent>
<child id="1" />
<child id="2" />
</parent>
<parent>
<child id="3" />
</parent>
</root>
So actually I've just created a solution but its a bit odd, ie generate an id for the parent node, and effectively we have a composite key of id attribute + id for the node
<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:key name="id_to_id" match="/root/parent/child" use="concat(#id,'|',generate-id(..))"/>
<xsl:template match="/root">
<root>
<xsl:for-each select="parent">
<parent>
<xsl:for-each select="child[generate-id() = generate-id(key('id_to_id',concat(#id,'|',generate-id(..)))[1])]">
<child id="{#id}"/>
</xsl:for-each>
</parent>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
is this the standard way to do this sort of thing?
heres another equally bizarre solution, still not sure this is very sensible, is there some sort of XPath parenthesis or something I can use?
<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:key name="id_to_id" match="child" use="#id"/>
<xsl:template match="/root">
<root>
<xsl:for-each select="parent">
<parent>
<xsl:variable name="children">
<xsl:copy-of select="child"/>
</xsl:variable>
<xsl:for-each select="msxsl:node-set($children)/child[generate-id() = generate-id(key('id_to_id',#id)[1])]">
<child id="{#id}"/>
</xsl:for-each>
</parent>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
now without meunchian grouping (which isnt what I want, but its a nice declarative approach), not suitable for anything but small groups of siblings.
<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="/root">
<root>
<xsl:for-each select="parent">
<parent>
<xsl:for-each select="child[not(#id = preceding-sibling::child/#id)]">
<child id="{#id}"/>
</xsl:for-each>
</parent>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>

xsl to remove specific attribute and add it as a child node instead

I have the following xml:
<Envelope>
<ABC>
<Hierarchy>
<Family>
<History> ... </History>
<Plan>
<Mom Name="Parent1">
<Child Name = "Child11">
<GrandChild Name="GC111" Label="" Sequence = "1">
<Attributes>... </Attributes>
</GrandChild>
</Child>
</Mom>
<Child Name = "ChildIndependent1">
<GrandChild Name="GCIndep12" Label="<Requested><Item1>68</Item1><Item2>69</Item2&g t;</Requested>" Sequence = "2" >
<Attributes>... </Attributes>
</GrandChild> </Child>
</Plan> </Family>
</Hierarchy> </ABC>
</Envelope>
Here is my current xsl i have on the xml to create dummy mom tags around independent child tags. It also converts the & lt; and & gt; to < and >
<xsl:stylesheet version="2.0" >
<xsl:output method="xml" indent="yes" use-character-maps="angle-brackets"/>
<xsl:character-map name="angle-brackets">
<xsl:output-character character="<" string="<"/>
<xsl:output-character character=">" string=">"/>
</xsl:character-map>
<xsl:template match="node()|#*">
<xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy>
</xsl:template>
<xsl:template match="//ABC/Hierarchy/Family">
<xsl:element name="Plan">
<xsl:for-each select="//ABC/Hierarchy/Family/Plan/*">
<xsl:if test="self::Mom">
<xsl:copy-of select="."/>
</xsl:if>
<xsl:if test="self::Child">
<xsl:element name="Mom">
<xsl:attribute name="Name">dummy</xsl:attribute>
<xsl:copy-of select="."/>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I need to modify the above xsl to create the LabelRequested tag with the content of the label attribute as a child to the GrandChild and remove the Label attribute itself as below above xsl already converts the & lt; and & gt; to < and >.
<Child Name = "ChildIndependent1">
<GrandChild Name="GCIndep12" Sequence = "2" >
<LabelRequested>
<Requested><Item1>68</Item1><Item2>69</Item2> </Requested>
</LabelRequested>
<Attributes>... </Attributes>
</GrandChild>
Any idea how I could change the above xsl to do this?
To minimize the example to the problem at hand, consider the following stylesheet:
XSLT 2.0
<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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="GrandChild">
<xsl:copy>
<xsl:apply-templates select="#* except #Label"/>
<LabelRequested>
<xsl:value-of select="#Label" disable-output-escaping="yes"/>
</LabelRequested>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<ABC>
<Hierarchy>
<Family>
<History> ... </History>
<Plan>
<Mom Name="Parent1">
<Child Name="Child11">
<GrandChild Name="GC111" Sequence="1">
<LabelRequested/>
<Attributes>... </Attributes>
</GrandChild>
</Child>
</Mom>
<Child Name="ChildIndependent1">
<GrandChild Name="GCIndep12" Sequence="2">
<LabelRequested><Requested><Item1>68</Item1><Item2>69</Item2></Requested></LabelRequested>
<Attributes>... </Attributes>
</GrandChild>
</Child>
</Plan>
</Family>
</Hierarchy>
</ABC>
</Envelope>
Note that this assumes your processor supports disable-output-escaping and that the processing chain allows it to execute it. Otherwise you would have to use either XSLT 3.0 or an extension function to parse the markup contained in the Label attribute.

How to copy attribute of a Node while injecting a Blank Node

I have a XML as
<ParentA Id="P1" Title="Parent1 Details>
<Child Att1="abcd" Id="Id1" Title="Public" Bind="Prop1" AccessItem="0">
</Child>
</Parent>
<ParentA Id="P2" Title="Parent2 Details>
<Child Att1="abcde" Id="Id2" Title="Public" Bind="Prop2" AccessItem="0">
</Child>
</Parent>
I want to Inject a Blank Tag ParentB as shown below ParentA.
<ParentA Id="P1" Title="Parent1 Details>
<ParentB>
<Child Att1="abcd" Id="Id1" Title="Public" Bind="Prop1" AccessItem="0">
</Child>
</ParentB>
</Parent>
When i add below XSL code, the attributes of ParentA are moved over to ParentB.
<xsl:template match="ParentA">
<xsl:copy>
<ParentB>
<xsl:apply-templates/>
</ParentB>
</xsl:copy>
</xsl:template>
I get the following output. No attributes on ParentA or ParentB.
<ParentA>
<ParentB>
<Child Att1="abcd" Id="Id1" Title="Public" Bind="Prop1" AccessItem="0">
</Child>
</ParentB>
</Parent>
When i add below code.
<xsl:template match="ParentA">
<xsl:copy>
<ParentB>
<xsl:apply-templates select="#*|node()" />
</ParentB>
</xsl:copy>
I get the following output with attributes of ParentA being copied over to ParentB
<ParentA>
<ParentB Id="P1" Title="Parent1 Details">
<Child Att1="abcd" Id="Id1" Title="Public" Bind="Prop1" AccessItem="0">
</Child>
</ParentB>
Expecting for some help
Try it this way:
<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="ParentA">
<xsl:copy>
<xsl:apply-templates select="#*" />
<ParentB>
<xsl:apply-templates select="node()" />
</ParentB>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

xslt on distinct nodes?

I have the following schema:
<parent>
<child id="1" name="Child 1 Version 1" />
</parent>
<parent>
<child id="2" name="Child 2 Version 1" />
</parent>
<parent>
<child id="1" name="Child 1 Version 2" />
</parent>
I want to handle only the last node for each id. Below is what I have tried based on some reading:
<xsl:for-each select="//parent/child">
<xsl:sort select="#id"/>
<xsl:if test="not(#id=following-sibling::*/#id)">
<xsl:element name="child">
<xsl:value-of select="#name"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
But it does not seem to work. My output still contains all three elements. Any ideas on what I can do to correct my issue?
That I want to only handle the last
node for each id. Below is what I have
tried based on some reading:
<xsl:for-each select="//parent/child">
<xsl:sort select="#id"/>
<xsl:if test="not(#id=following-sibling::*/#id)">
<xsl:element name="child">
<xsl:value-of select="#name"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
But it does not seem to work. My
output still contains all three of the
elements. Any ideas on what I can do
to correct my issue?
The problem with this code is that even though the nodes are in a sorted node-set, their following-sibling s are still the ones in the document.
In order for this code to work, one would first create an entirely new document in which the nodes are sorted in the desired way, then (in XSLT 1.0 it is necessary to use the xxx:node-set() extension on the produced RTF to make it an ordinary XML document) on this document the nodes have their siblings as desired.
Solution:
This transformation presents one possible XSLT 1.0 solution that does not require the use of extension functions:
<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:key name="kchildById" match="child" use="#id"/>
<xsl:template match="/*">
<t>
<xsl:apply-templates select=
"*/child[generate-id()
=
generate-id(key('kchildById',
#id)[last()]
)
]
"/>
</t>
</xsl:template>
<xsl:template match="child">
<child>
<xsl:value-of select="#name"/>
</child>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML fragment (wrapped in a top element to become well-formed XML document and adding a second version for id="2"):
<t>
<parent>
<child id="1" name="Child 1 Version 1" />
</parent>
<parent>
<child id="2" name="Child 2 Version 1" />
</parent>
<parent>
<child id="1" name="Child 1 Version 2" />
</parent>
<parent>
<child id="2" name="Child 2 Version 2" />
</parent>
</t>
produces the wanted result:
<t>
<child>Child 1 Version 2</child>
<child>Child 2 Version 2</child>
</t>
Do note: the use of the Muenchian method for grouping.
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kParentByChildId" match="parent" use="child/#id"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="parent[count(.|key('kParentByChildId',
child/#id)[last()]) != 1]"/>
</xsl:stylesheet>
Output:
<root>
<parent>
<child id="2" name="Child 2 Version 1"></child>
</parent>
<parent>
<child id="1" name="Child 1 Version 2"></child>
</parent>
</root>
Note. Grouping by #id, selecting last of the group.
Edit: Just in case this is confusing. Above stylesheet means: copy everything execpt those child not having the last #id of the same kind. So, it's not selecting the last of the group, but as reverse logic, striping not last in the group.
Second. Why yours is not working? Well, because of the following-sibling axis. Your method for finding the first of a kind is from an old time where there was few processor implementing keys. Now those days are gone.
So, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="t">
<xsl:for-each select="parent/child">
<xsl:sort select="#id"/>
<xsl:if test="not(#id=following::child/#id)">
<xsl:element name="child">
<xsl:value-of select="#name"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
<child>Child 1 Version 2</child>
<child>Child 2 Version 1</child>
Note: following axis, because child elements have not siblings.