Possible Duplicate:
Converting input xml using xslt to other XML
I am astarter in XSLT. I have looked some codes for the task i was interested on and built some logic but i could not get into the desired output. I am glad if i could get a help.
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<t>
<Data>
<CD>
<Artist>xxx.yyy</Artist>
<song>abc</song>
</CD>
<CD>
<Artist>xxx.zzz</Artist>
<song>cba</song>
</CD>
<CD>
<Artist>aaa.kkk</Artist>
<song>123</song>
</CD>
<CD>
<Artist>aaa.lll</Artist>
<song>456</song>
</CD>
<CD>
<Artist>ddd</Artist>
<song>1234</song>
</CD>
</Data>
<Music>
<music_no>E123</music_no>
<music_type>outdoor</music_type>
<Artist>bat.ball</Artist>
<value>0000</value>
</Music>
<Music>
<music_no>E123</music_no>
<music_type>outdoor</music_type>
<Artist>bat.stone</Artist>
<value>0001</value>
</Music>
<Music>
<music_no>E111</music_no>
<music_type>outdoor</music_type>
<Artist>board.coins</Artist>
<value>0002</value>
</Music>
<Music>
<music_no>E111</music_no>
<music_type>outdoor</music_type>
<Artist>board.ball</Artist>
<value>0003</value>
</Music>
<Music>
<music_no>E001</music_no>
<music_type>indoor</music_type>
<Artist>bat.ball</Artist>
<value>8888</value>
</Music>
<Music>
<music_no>E001</music_no>
<music_type>indoor</music_type>
<Artist>bat.stone</Artist>
<value>9999</value>
</Music>
<Music>
<music_no>E111</music_no>
<music_type>indoor</music_type>
<Artist>board.coins</Artist>
<value>0001</value>
</Music>
<Music>
<music_no>E111</music_no>
<music_type>indoor</music_type>
<Artist>bat</Artist>
<value>0001</value>
</Music>
</t>
Expected Output:
<?xml version="1.0" encoding="UTF-8"?>
<version_3>
<information>
<xxx>
<yyy>abc</yyy>
<zzz>cba</zzz>
</xxx>
<aaa>
<kkk>123</kkk>
<lll>456</lll>
</aaa>
<ddd>1234</ddd>
</information>
<information>
<bat>
<ball>0000</ball>
<stone>0001</stone>
</bat>
<board>
<coins>0002</coins>
<ball>0003</ball>
</board>
<bat>
<ball>8888</ball>
<stone>9999</stone>
</bat>
<board>
<coins>0001</coins>
</board>
</information>
<information>
<bat>0001</bat>
</information>
</version_3>
Edited Expected Output:
Expected Output:
<?xml version="1.0" encoding="UTF-8"?>
<version_3>
<information>
<xxx>
<yyy>abc</yyy>
<zzz>cba</zzz>
</xxx>
<aaa>
<kkk>123</kkk>
<lll>456</lll>
</aaa>
<ddd>1234</ddd>
</information>
<information>
<bat>
<ball>0000</ball>
<stone>0001</stone>
</bat>
</information>
<information>
<board>
<coins>0002</coins>
<ball>0003</ball>
</board>
<board>
<coins>0001</coins>
<bat>0001</bat>
</board>
</information>
<information>
<bat>
<ball>8888</ball>
<stone>9999</stone>
</bat>
</information>
</version_3>
In the above Input XML: you can notice “CD” element and values in it, similarly “music”. I want to get an output that looks like,
Sample output for “CD”:
<information>
<xxx>
<yyy>abc</yyy>
<zzz>cba</zzz>
</xxx>
<aaa>
<kkk>123</kkk>
<lll>456</lll>
</aaa>
<ddd>1234</ddd>
</information>
I could Achieve this to some extent by Muenchian Grouping. But,
The next Element “Music” has sub elements in which, the first two elements “music_no” & “music_type” get matched then the Values in the “Artist” has to be grouped. If they arent matched they have to be seperately grouped.
Sample o/p for Music:
<information>
<bat>
<ball>0000</ball>
<stone>0001</stone>
</bat>
<board>
<coins>0002</coins>
<ball>0003</ball>
</board>
<bat>
<ball>8888</ball>
<stone>9999</stone>
</bat>
<board>
<coins>0001</coins>
</board>
</information>
<information>
<bat>0001</bat>
</information>
I could not achieve the second part as it is bit tricky with iterations. Help is Appreciated.
Note: for the “Music” element if the Value in the Artist resembles the same with the corresponding nodes but having no “.” then that Value has to be seperately grouped that is outside “information” and shall have new “information”
My Code that i Worked on:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:key name="kBychildName" match="CD" use="name(Artist/*[1])"/>
<xsl:key name="kByAttribs" match="Artist" use="concat(../@music_no, '+', ../@music_type)"/>
<xsl:key name="kChildByAttribsAndArtist" match="Artist/*" use="concat(../../@music_no, '+', ../../@music_type, '+', name())"/>
<xsl:template match="/">
<version_3>
<information>
<xsl:variable name="var1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:apply-templates mode="pass2" select="ext:node-set($var1)/* [generate-id()=generate-id(key('kBychildName',name(Artist/*[1]))[1]) or not(Artist/*)]"/>
</information>
<information>
<xsl:variable name="var2">
<xsl:apply-templates/>
</xsl:variable>
<xsl:apply-templates mode="pass3" select="ext:node-set($var2)/*/* [generate-id() = generate-id(key('kByAttribs', concat(../@music_no, '+', ../@music_type) ) [1])] "/>
</information>
</version_3>
<!--xsl:copy-of select="//msg_debug"/-->
</xsl:template>
<xsl:template match="CD[contains(Artist,'.')]">
<CD>
<Artist>
<xsl:element name="{substring-before(Artist, '.')}">
<xsl:element name="{substring-after(Artist, '.')}">
<xsl:value-of select="song"/>
</xsl:element>
</xsl:element>
</Artist>
</CD>
</xsl:template>
<xsl:template match="CD">
<CD>
<Artist>
<xsl:element name="{Artist}">
<xsl:value-of select="song"/>
</xsl:element>
</Artist>
</CD>
</xsl:template>
<xsl:template match="CD" mode="pass2">
<xsl:apply-templates select="*/*[1]" mode="pass2"/>
</xsl:template>
<xsl:template match="Artist/*" mode="pass2">
<xsl:copy>
<xsl:copy-of select="self::*[not(*)]/text()|key('kBychildName', name())/*/*/*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Music[contains(Artist, '.')]">
<Music music_no="{music_no}" music_type="{music_type}">
<Artist>
<xsl:element name="{substring-before(Artist, '.')}">
<xsl:element name="{substring-after(Artist, '.')}">
<xsl:value-of select="value"/>
</xsl:element>
</xsl:element>
</Artist>
</Music>
</xsl:template>
<xsl:template match="Music">
<Music music_no="{music_no}" music_type="{music_type}">
<Artist>
<xsl:element name="{Artist}">
<xsl:value-of select="value"/>
</xsl:element>
</Artist>
</Music>
</xsl:template>
<xsl:template match="Artist" mode="pass3">
<!--Artist-->
<xsl:apply-templates mode="pass3" select="*[generate-id() =generate-id(key('kChildByAttribsAndArtist', concat(../../@music_no, '+', ../../@music_type,'+', name()))[1] ) ]"/>
<xsl:copy-of select="key('kByAttribs',concat(../@music_no, '+', ../@music_type) )/*[not(*)] "/>
<!--/Artist-->
</xsl:template>
<xsl:template match="Artist/*" mode="pass3">
<xsl:element name="{name()}">
<xsl:copy-of select="key('kChildByAttribsAndArtist', concat(../../@music_no, '+', ../../@music_type, '+', name()) )/* "/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I am glad to explain the problem again, if found difficult. As a beginners to XSLT i hope you can help me.
A simple question deserves a simple answer. This XSLT 1.0 style-sheet …
… when applied to your sample input, will produce your required expected output.
Explanation
A single key is used to group both Music and CD elements. In the case of music, there is a further division into music_no and music_type. The first output information node is derived from a muenchian grouping of the CD elements. The orphaned artists within the first information node are produced by the instruction.
A common pair CD|Music templates is used for all grouped-artists and orphaned-artists node production. The first of the template pair, with predicate [substring-before(Artist,’.’)!=”] is used for the grouped-artists, and the other for the orphaned artists.
The production of the second information node, goes like the first, except it is based on input Music nodes and excludes orphaned artists.
The production of the third information node is derived from orphaned Music nodes.
By ‘orphaned’, I mean nodes whose Artist value does not contain a dot character.