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>
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 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>
The header and child elements are the children of parent. Can I keep header and its children as it is and the remaining subchild nodes of child are to be added to a new element?
<parent>
<header>
<left></left>
<right></right>
</header>
<child>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
</child>
<child>
<subchild></subchild>
<subchild></subchild>
</child>
<child>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
</child>
</parent>
Is there any way where I can generate the below output?
<parent>
<header>
<left></left>
<right></right>
</header>
<element>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
<subchild></subchild>
</element>
</parent>
Make a new element and pass remaining all sub childs nodes of child.
This transform will do what you need. It is a basic identity transform with a special case for child elements, which are simply replaced by all their children.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="child">
<xsl:copy-of select="*"/>
</xsl:template>
</xsl:stylesheet>
output
<parent>
<header>
<left/>
<right/>
</header>
<subchild/>
<subchild/>
<subchild/>
<subchild/>
<subchild/>
<subchild/>
<subchild/>
<subchild/>
</parent>
For y our XML file you may use this XSLT schema for get result like you wnat (use HTML for pretty look in browser)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="parent">
<div id="parent">
<header>
<div id="left"><xsl:value-of select="header/left/text()"/></div>
<div id="right"><xsl:value-of select="header/right/text()"/></div>
</header>
<ul id="elements">
<xsl:for-each select="child/subchild">
<li><xsl:value-of select="text()"/></li>
</xsl:for-each>
</ul>
</div>
</xsl:template>
</xsl:stylesheet>
Result looks like this:
Left block
Right Block
Child 1 - Subchild 1
Child 1 - Subchild 2
Child 1 - Subchild 3
Child 2 - Subchild 1
Child 2 - Subchild 2
Child 3 - Subchild 1
Child 3 - Subchild 2
Child 3 - Subchild 3
For complite make result as in you question - use this XSLT code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="parent">
<parent>
<xsl:value-of select="header/node()"/>
<elements>
<xsl:for-each select="child/subchild">
<xsl:value-of select="node()"/>
</xsl:for-each>
</elements>
</parent>
</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>
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.