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>
Related
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'"/>.
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.
I have a situation where I need to select the nodes based on certain conditions and then apply Muenchian grouping to get unique values. I am on XSLT 1.0
<Parent>
<Child>
<A></A>
<B></B>
<C></C>
</Child>
<Child>
<A></A>
<B></B>
<C></C>
</Child>
<Child>
<A></A>
<B></B>
<C></C>
</Child>
</Parent>
The Condition that I have for use attribute is:
If A is not blank, use A. Otherwise use B.
I had thought of a solution that would work for Fixed Width values for Node A and B. So If A and B are of length n, I can do something like this:
<xsl:key name="groupKey" match="/Parent/Child" use="substring(concat(A,B),1,n)">
So, If A is not present, I can use B. But I haven't been able to figure out how to use string-length() to derive the expression for variable lengths for A and B. Any Ideas?
In case you want to use the concat() function:
use="concat(A, B[not(string(../A)])"
And if by blank you mean empty or whitespace-only, then use:
use="concat(A, B[not(normalize-space(../A)])"
Finally, if you really want to use both concat() and substring() (and this is really tricky, so I don't recommend it):
concat(substring(A, 1 div string(A) or 0),
substring(B, 1 div not(string(A)))
)
Here is a complete example for the last case, showing what the call to concat() returns:
XML document:
<t>
<p>
<A>xxx</A>
<B>yyy</B>
</p>
<p>
<A/>
<B>zzz</B>
</p>
</t>
Transformation (just outputs the results of the evaluated expressions):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="p">
<xsl:value-of select=
"concat(substring(A, 1 div string(A) or 0),
substring(B, 1 div not(string(A)))
)"/>
</xsl:template>
</xsl:stylesheet>
Wanted (correct) result produced:
xxx
zzz
How about:
use="A | B[not(string(../A))]"
Here's another option that uses a different use.
Example...
XML Input
<Parent>
<Child>
<A>both</A>
<B>both</B>
<C>c</C>
</Child>
<Child>
<A>a</A>
<B></B>
<C>c</C>
</Child>
<Child>
<A></A>
<B>b</B>
<C>c</C>
</Child>
</Parent>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="child" match="Child" use="(A[string()]|B[string()])[1]"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each select="Child[count(.|key('child',(A[string()]|B[string()])[1])[1])=1]">
<group key="{(A[string()]|B[string()])[1]}">
<xsl:apply-templates select="key('child',(A[string()]|B[string()])[1])"/>
</group>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XML Output
<Parent>
<group key="both">
<Child>
<A>both</A>
<B>both</B>
<C>c</C>
</Child>
</group>
<group key="a">
<Child>
<A>a</A>
<B/>
<C>c</C>
</Child>
</group>
<group key="b">
<Child>
<A/>
<B>b</B>
<C>c</C>
</Child>
</group>
</Parent>
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>
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>