Add sub child of a child to a element - xslt

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>

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>

Select Nodes using key() based on Condition

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>

Split first child node from the rest using XSLT

Can anyone help me to split the child nodes using XSLT. Only child1 should be split from rest of the children.
<parent>
<child1>
<child2>
<child3>
<parent>
output:
<parent>
<child1>
<element>
<child2>
<child3>
</element>
<parent>
Here's a simple solution.
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<parent>
<xsl:copy-of select="child1"/>
<element>
<xsl:copy-of select="*[not(self::child1)]"/>
</element>
</parent>
</xsl:template>
</xsl:stylesheet>
...is applied against the provided XML:
<parent>
<child1/>
<child2/>
<child3/>
</parent>
...the wanted result is produced:
<parent>
<child1/>
<element>
<child2/>
<child3/>
</element>
</parent>
If I understand correctly, you have xml like this:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="data.xsl"?>
<data>
<parent>
<child>123</child>
<child>345</child>
<child>678</child>
</parent>
</data>
And want to show it like this:
<span>123</span>
<ul>
<li>345</li>
<li>678</li>
</ul>
If it is right, use next code:
<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">
<strong><xsl:value-of select="child[1]/text()"/></strong>
<ul>
<xsl:for-each select="child">
<xsl:if test="position() != 1">
<li><xsl:value-of select="text()"/></li>
</xsl:if>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>

How to return text of node without child nodes text

I have xml like:
<item id="1">
<items>
<item id="2">Text2</item>
<item id="3">Text3</item>
</items>Text1
</item>
How to return text of <item id="1">('Text1')?
<xsl:value-of select="item/text()"/> returns nothing.
My XSLT is:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="item"/>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<xsl:value-of select="text()"/>
</xsl:template>
</xsl:stylesheet>
I dont know what else to type to commit my edits
How to return text of <item id="1">('Text1')? <xsl:value-of
select="item/text()"/> returns nothing.
The item element has more than one text-node children and the first of them happens to be a all-whitespace one -- this is why you get "nothing".
One way to test if the string value of a node isn't all-whitespace is by using the normalize-space() function.
In a single Xpath expression, you want this:
/*/text()[normalize-space()][1]
Here is a complete transformation the result of which is the desired text node:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:copy-of select="text()[normalize-space()][1]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<item id="1">
<items>
<item id="2">Text2</item>
<item id="3">Text3</item>
</items>Text1
</item>
the wanted, correct result is produced:
Text1
This should generally work:
<xsl:apply-templates select="item/text()" />
Incorporated into your XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="item_key" match="item" use="."/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<html>
<body>
<ul>
<xsl:apply-templates select="item"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<li>
<xsl:apply-templates select="text()"/>
</li>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, the result is:
<html>
<body>
<ul>
<li>Text1
</li>
</ul>
</body>
</html>
Alternatively, this should work as well:
<xsl:copy-of select="item/text()" />