I'm working on an xslt which will be used to translate a set of xmls files, a sample input xml is something like :-
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="A12">
<bookattribute name="abc" price="200" />
<bookattribute name="xyz" price="300" />
<bookattribute name="pqr" price="400" />
<bookattribute name="lnz" price="500" />
</book>
<book id="B12">
<bookattribute name="cpz" price="300" />
<bookattribute name="rts" price="800" />
</book>
<novel id="AA12">
<bookattribute name="yps" price="200" />
<bookattribute name="udv" price="600" />
</novel>
<!-- book node with id=AA12 may or may not be present in the xml -->
<book id="AA12">
<bookattribute name="abc" price="200" />
<bookattribute name="aps" price="600" />
</book>
</bookstore>
i want to transform it by creating copy of node "book" with attribute id="A12" based on condition that , if node book with attribute id=AA12 does not exist, then create copy of node "book"(attribute id="A12") and in the copy change id to "AA12" (thus its copy of node with change of attribute value), but if node with book(id="AA12") already exists in the xml then add those child nodes of book(id=A12) which are not present in "book"(attribute id="AA12"), Here I want to add only those child nodes(with bookattribute "name" as the key ) which are not present in book@id=AA12) , for example if child node bookattribute name="abc" is already present in book@id=AA12 then it should not be added again to it, further their may be certain other element nodes like novel or ebook which may also have attribute id="AA12" these nodes must be copied as it is.
so output, in case (input file above contains node "book"(attribute id="AA12") )
<bookstore>
<book id="A12">
<bookattribute name="abc" price="200" />
<bookattribute name="xyz" price="300" />
<bookattribute name="pqr" price="400" />
<bookattribute name="lnz" price="500" />
</book>
<book id="B12">
<bookattribute name="cpz" price="300" />
<bookattribute name="rts" price="800" />
</book>
<novel id="AA12">
<bookattribute name="yps" price="200" />
<bookattribute name="udv" price="600" />
</novel>
<book id="AA12">
<bookattribute name=aps price=600 />
<bookattribute name="abc" price="200" />
<bookattribute name="xyz" price="300" />
<bookattribute name="pqr" price="400" />
<bookattribute name="lnz" price="500" />
</book>
</bookstore>
output , in case (input file does not contain node "book"(attribute id="AA12") )
<bookstore>
<book id="A12">
<bookattribute name="abc" price="200" />
<bookattribute name="xyz" price="300" />
<bookattribute name="pqr" price="400" />
<bookattribute name="lnz" price="500" />
</book>
<book id="B12">
<bookattribute name="cpz" price="300" />
<bookattribute name="rts" price="800" />
</book>
<novel id="AA12">
<bookattribute name="yps" price="200" />
<bookattribute name="udv" price="600" />
</novel>
<book id="AA12">
<bookattribute name="abc" price="200" />
<bookattribute name="xyz" price="300" />
<bookattribute name="pqr" price="400" />
<bookattribute name="lnz" price="500" />
</book>
</bookstore>
I have been able to create xslt as below which creates copy with changed attributes as required , but im unable to work out addition of child nodes in case node book with id="AA12" exists, any pointers would be helpful
my xslt:-
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="comment()|processing-instruction()"> <xsl:comment> <xsl:value-of select="."/> </xsl:comment> </xsl:template> <xsl:template match="book[@id='A12']"> <xsl:copy> <xsl:attribute name="id">A12</xsl:attribute> <xsl:apply-templates /> </xsl:copy> <xsl:copy> <xsl:attribute name="id">AA12</xsl:attribute> <xsl:apply-templates /> </xsl:copy> </xsl:template> </xsl:stylesheet>
Please be careful when posting XML, your input document is not well-formed.
Use a key to identify book
elements by their ID. Then, the rationale is as follows:
bookstore
element and copy it to the output bookstore
, first copy everything from the input document, except the book
element where id="AA12"
<book id="AA12">
in all cases, and copy into it all the children of (potentially) book id="AA12"
and book id="A12"
XSLT Stylesheet
EDIT I have edited the stylesheet in response to
this xslt may require minor fix as it may duplicate the child nodes, for example if is already present in book@id="A12" and "AA12", a filter to avoid copying duplicate child nodes would be required
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="book-by-id" match="book" use="@id"/>
<xsl:template match="bookstore">
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::book and @id = 'AA12')]"/>
<book id="AA12">
<xsl:copy-of select="key('book-by-id','AA12')/*"/>
<xsl:for-each select="key('book-by-id','A12')/*">
<xsl:if test="not(./@name = key('book-by-id','AA12')/*/@name)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</book>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
Alternative solution:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="book-by-id" match="book" use="@id"/>
<xsl:key name="book-attribute-by-name" match="bookattribute" use="@name"/>
<xsl:template match="bookstore">
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(@id = 'AA12')]"/>
<book id="AA12">
<xsl:apply-templates select="key('book-by-id','AA12')/*,key('book-by-id','A12')/*"/>
</book>
</xsl:copy>
</xsl:template>
<xsl:template match="key('book-by-id','A12')/*[@name = key('book-by-id','AA12')/*/@name]"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
XML Output
In case there already was a book
element with id="AA12"
:
<bookstore>
<book id="A12">
<bookattribute name="abc" price="200"/>
<bookattribute name="xyz" price="300"/>
<bookattribute name="pqr" price="400"/>
<bookattribute name="lnz" price="500"/>
</book>
<book id="B12">
<bookattribute name="cpz" price="300"/>
<bookattribute name="rts" price="800"/>
</book>
<!-- book node with id=AA12 may or may not be present in the xml -->
<book id="AA12">
<bookattribute name="abc" price="200"/>
<bookattribute name="xyz" price="300"/>
<bookattribute name="pqr" price="400"/>
<bookattribute name="lnz" price="500"/>
<bookattribute name="aps" price="600"/>
</book>
</bookstore>
In case there was no such element:
<bookstore>
<book id="A12">
<bookattribute name="abc" price="200"/>
<bookattribute name="xyz" price="300"/>
<bookattribute name="pqr" price="400"/>
<bookattribute name="lnz" price="500"/>
</book>
<book id="B12">
<bookattribute name="cpz" price="300"/>
<bookattribute name="rts" price="800"/>
</book>
<!-- book node with id=AA12 may or may not be present in the xml -->
<book id="AA12">
<bookattribute name="abc" price="200"/>
<bookattribute name="xyz" price="300"/>
<bookattribute name="pqr" price="400"/>
<bookattribute name="lnz" price="500"/>
</book>
</bookstore>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.