Reputation: 181
I have this input file:
<root>
<library id="L1">
<shelf1 id="1">
<book id="1" action="borrow">
<attributes>
<user>John</user>
</attributes>
<other1>y</other1>
</book>
<book id="1" action="extend">
<attributes>
<user>Woo</user>
<length>3</length>
</attributes>
<other2>y</other2>
</book>
<book id="1" action="extend">
<attributes>
<length>2</length>
<condition>ok</condition>
</attributes>
<other3>y</other3>
</book>
<book id="2" action="borrow">...</book>
</shelf1>
<shelf2>...</shelf2>
</library>
</root>
Expected output:
<root>
<library id="L1">
<shelf1 id="1">
<book id="1" action="borrow">
<attributes>
<user>Woo</user>
<length>2</length>
<condition>ok</condition>
</attributes>
<other1>y</other1>
<other2>y</other2>
<other3>y</other3>
</book>
<book id="2" action="borrow">...</book>
</shelf1>
<shelf2>...</shelf2>
</library>
</root>
My XSL:
<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()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="library/*/*[1]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<attributes>
<xsl:for-each-group select="attributes/*" group-by="name()">
<xsl:sort select="current-grouping-key()"/>
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</attributes>
<xsl:apply-templates select="*[not(self::attributes)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"library/*/*
[@action='extend' and following-sibling::*[1][@action='extend']
or preceding-sibling::*[@action='borrow']]"/>
</xsl:stylesheet>
For every node with action=borrow
followed by one or more node with action=extend
action=borrow
. attributes
children together such that it will have all the unique attributes from the siblings with the latest value.Please let me know how to fix this transformation using XSLT 2.0 ?
Thanks very much.
Kind regards, John
Upvotes: -1
Views: 374
Reputation: 12729
You've asked a number of similar questions. Maybe it's time to buy a book and spend a couple of days reading? This is probably the last time I am going to answer a similar question from you. You should be able to learn from answers so that you never have to ask similar questions. If you are not learning, you have to ask yourself: "What is holding you back?"
Any way, lets have a look at this style-sheet. In the explanation, I will refer to points as numbers within xml or xpath comments. For example point 1 is demarcated <!-- 1 --> or if inside an xpath expression, then (: 1 :) . Obviously, remove the comments for production.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:John="http://stackoverflow.com/questions/11463900"
exclude-result-prefixes="xsl xs fn John">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(local-name(),'shelf')] (: 1 :)">
<xsl:copy>
<xsl:apply-templates select="@*" />
<!-- 2 -->
<xsl:apply-templates select="
book[@action='extend'] (: 3 :)
[not( preceding-sibling::book[@action='borrow'])] (: 4 :)" />
<!-- 5 -->
<xsl:for-each-group
select="book[@action='borrow'] (: 6 :)
| (: 7 :)
book[@action='extend']
[preceding-sibling::book[@action='borrow']] (: 8 :)"
group-starting-with="book[@action='borrow']">
<xsl:for-each select="current-group()[1]">
<xsl:copy> <!-- 9 -->
<xsl:apply-templates select="@*" />
<xsl:call-template name="merge-books-deeply"> <!-- 10 -->
<xsl:with-param name="books" select="current-group()" />
<xsl:with-param name="name-path" select="()" />
</xsl:call-template>
</xsl:copy>
</xsl:for-each>
</xsl:for-each-group>
<xsl:apply-templates select=" (: 11 :)
node()[ not( self::book[@action=('borrow','extend')])]" />
</xsl:copy>
</xsl:template>
<xsl:function name="John:children-on-path" as="element()*">
<xsl:param name="base" as="element()*" /> <!-- 12 -->
<xsl:param name="path" as="xs:string*" /> <!-- 13 -->
<xsl:choose>
<xsl:when test="fn:empty($base)">
<xsl:sequence select="()" />
</xsl:when>
<xsl:when test="fn:empty($path)">
<xsl:copy-of select="$base/*" /> <!-- 14 -->
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="John:children-on-path(
$base/*[name()=$path[1]], (: 15 :)
$path[position() ne 1])" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template name="merge-books-deeply">
<xsl:param name="books" as="element()*" />
<xsl:param name="name-path" as="xs:string*" />
<xsl:for-each-group
select="John:children-on-path($books,$name-path)"
group-by="name()"> <!-- 16 -->
<xsl:for-each select="current-group()[last()]" > <!-- 17 -->
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:call-template name="merge-books-deeply"> <!-- 18 -->
<xsl:with-param name="books" select="$books" />
<xsl:with-param name="name-path" select="$name-path,name()" />
</xsl:call-template>
<xsl:apply-templates select="text()" />
</xsl:copy>
</xsl:for-each>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3