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

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>

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>

I need to change an xml attribute value conditionaly from an external xml using Xsl

I have the following 2 xml files,
Mapping.xml
<xml>
<map ordinal="0" reverse="xxx" forward="ThisIsXxx" />
<map ordinal="0" reverse="yyy" forward="ThisIsYyy" />
<map ordinal="0" reverse="zzz" forward="thisIsZzz" />
<map ordinal="0" reverse="xx1" forward="ThisIsXx1Info" />
<map ordinal="0" reverse="yy1" forward="ThisIsYy1Info" />
<map ordinal="0" reverse="zz1" forward="ThisIsZz1Info" />
</xml>
and an input xml file with a series of elements with some of them having a 'name' attribute
second.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<Children>
<child name="xxx">
<info name="xx1">
</info>
</child>
<child name="yyy">
<info name="yy1">
</info>
</child>
<child name="zzz">
<info name="zz1">
</info>
</child>
</Children> <!-- Added by edit -->
</xml>
What I need is if direction is 'forward' then the second.xml files #name to be set to the value from #forward from the Mapping.xml such as the following,
output.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<Children>
<child name="ThisIsXxx">
<info name="ThisIsXx1Info">
</info>
</child>
<child name="ThisIsZzz">
<info name="ThisIsYy1Info">
</info>
</child>
<child name="ThisIsYyy">
<info name="ThisIsZz1Info">
</info>
</child>
</xml>
Xslt file I have is setting all #name="" but not getting any values from the external file..
<?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"/>
<!-- get the mapping db -->
<xsl:variable name="mapDb" select="document('Mapping.xml')" />
<xsl:variable name="mapFwd" select="forward" />
<!-- for all #name find the #mapdb/*/#reverse == #name and set #name=#mapDb/*/forward -->
<xsl:template match="*/#name">
<xsl:choose>
<xsl:when test="$mapFwd='forward'">
<xsl:attribute name="name">
<xsl:value-of select="$mapDb/xml/map[#reverse='{#name}']/#forward"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="name">
<xsl:value-of select="$mapDb/xml/map[#forward='{#name}']/#reverse"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--Copy Everything unchanged-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would appreciate some help.
I think you want to use current() e.g. $mapDb/xml/map[#reverse=current()] in your comparisons in the template matching the #name attribute and of course the variable should be initialized with <xsl:variable name="mapFwd" select="'forward'"/>.

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.

Copy XML and rename some of its nodes and nested nodes

I need to copy everything to new XML except some elements which I need to rename.
All "child" nodes which are childs of "node1" I need to rename to "children" and "child" nodes which are childs of "node2" I need to rename to "kid".
Perfect solution will be using
match="/root/node1/descendant::child"
But as I found in Michael Kay response in topic http://comments.gmane.org/gmane.text.xml.saxon.help/14956
A pattern of the form /descendant::m__id[1] is not legal in either
XSLT 1.0 or XSLT 2.0, though it becomes legal in XSLT 3.0.
Do You have any suggestions how to do that in XSLT 2.0 without doing what I made below? Because I don't know how many nesting there will be exactly.
Here is my xml example
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<node2>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node2>
</root>
And XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.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="/root/node1/child">
<xsl:element name="children">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node1/child/subnode/child">
<xsl:element name="children">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node2/child">
<xsl:element name="kid">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node2/child/subnode/child">
<xsl:element name="kid">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
You can use //child in a pattern:
<xsl:template match="/root/node1//child">
<xsl:element name="children">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node2//child">
<xsl:element name="kid">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
I would use literal result elements instead of xsl:element:
<xsl:template match="/root/node1//child">
<children>
<xsl:apply-templates select="#*|node()"/>
</children>
</xsl:template>
<xsl:template match="/root/node2//child">
<kid>
<xsl:apply-templates select="#*|node()"/>
</kid>
</xsl:template>
Would this work for you?
<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="child">
<children>
<xsl:apply-templates select="#*|node()"/>
</children>
</xsl:template>
<xsl:template match="child[ancestor::node2]">
<kid>
<xsl:apply-templates select="#*|node()"/>
</kid>
</xsl:template>
</xsl:stylesheet>
This solution doesn't use any predicates or axes at all, has the shortest match patterns, and thus is more efficient than one that uses predicates.
It also produces the expected correct result when <node1> has descendents or ancestors <node2> -- something which the remaining solutions don't handle at all.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node1" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="node1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node2" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="node2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child" mode="node1">
<children>
<xsl:apply-templates select="node()|#*" mode="#current"/>
</children>
</xsl:template>
<xsl:template match="child" mode="node2">
<kid>
<xsl:apply-templates select="node()|#*" mode="#current"/>
</kid>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<root>
<node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<node2>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node2>
</root>
the wanted, correct result is produced:
<root>
<node1>
<children>
<subnode>
<children/>
</subnode>
</children>
<children>
<subnode>
<children/>
</subnode>
</children>
</node1>
<node2>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
</node2>
</root>
When the same transformation is applied on this XML document, where <node1> and <node2> are descendents or ancestors of each other:
<root>
<node1>
<child>
<subnode>
<node2>
<child></child>
<node1>
<child/>
</node1>
</node2>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<node2>
<child>
<subnode>
<child></child>
</subnode>
</child>
<node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node2>
</root>
again the correct and wanted result (the replacement-name of <child> is determined by its closest ancestor that is either <node1> or <node2>) is produced:
<root>
<node1>
<children>
<subnode>
<node2>
<kid/>
<node1>
<children/>
</node1>
</node2>
</subnode>
</children>
<children>
<subnode>
<children/>
</subnode>
</children>
</node1>
<node2>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
<node1>
<children>
<subnode>
<children/>
</subnode>
</children>
</node1>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
</node2>
</root>

xslt move attributes in child as element to parent node

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>