 |
| XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead. |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

July 31st, 2012, 06:05 AM
|
|
Registered User
|
|
Join Date: Jul 2012
Posts: 9
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Getting rid of Duplicate nodes
Hello Everyone,
I have pretty similar looking problem which have got many solutions over the web but none of them have worked well for me so far. Here's the problem statement:
XML file version "1.0":
<enums>
<enum name="enumOne">
<val v="1"/>
</enum>
<enum name="enumTwo">
<val v="2"/>
</enum>
<enum name="enumThree">
<val v="3"/>
</enum>
<table>
<child name="child1">
<type t="enumOne"/>
..........
.........
</child>
<child name="child2">
<type t="enumTwo"/>
..........
.........
</child>
<child name="child3">
<type t="enumOne"/>
..........
.........
</child>
</table>
XSL version "1.0": It would traverse through child node and retrieve the enum type i.e. enumXXX from <type> tag. and find it's definition from <enums>
<xsl:for-each select="/child/type">
<xsl:call-template name="getenumdefs">
<xsl:with-param name="enumname">
<xsl:value-of select="@t"/>
</xsl:with-param>
</xsl:call-template>
<xsl:for-each>
<xsl:template name="getenumdefs">
<xsl:param name="enumname"/>
<xsl:for-each select="/enums/enum">
<xsl:if test="$enumname = '$name'">
<xsl:value-of select="val/@v"/>
</xsl:if>
</xsl:for-each>
Problem with this approach is, it would print the duplicate entries for "enumOne" as both child1 and child3 uses it. There are multiple solutions available in the form of preceding-sibling or checking generate-id(), but nothing had really worked well for this xml/xsl. Appreciate if anyone can provide any inputs/suggestions.
Last edited by sureshvadali; August 1st, 2012 at 04:44 AM..
|
|

July 31st, 2012, 06:32 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
Are you constrained to use XSLT 1.0? Are you familiar with Muenchian grouping?
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|

July 31st, 2012, 07:03 AM
|
|
Registered User
|
|
Join Date: Jul 2012
Posts: 9
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
The other alternative could be: create a flag variable for the first occurrence of enum and flag it. The subsequent calls to the template getenumdefs would check if the flag is set for the respective enum and skip it if flagged. But am not sure about the feasibility of such implementation in XSL.
|
|

July 31st, 2012, 07:25 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
Flag variables? No, you are thinking much too procedurally.
Why didn't you answer my questions?
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|

August 1st, 2012, 09:06 AM
|
|
Registered User
|
|
Join Date: Jul 2012
Posts: 9
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Sorry Michael, I replied to my original thread instantly after posting it, but it seems there was a delay in capturing it into the thread. I just saw your responses, thanks for that. Right most of XSL's have been written in 1.0 and the source XML also adheres to 1.0 version moving to 2.0 might be tricky?
Though am not familiar about Muenchian grouping but I went through some examples and figured out the below approach:
<xsl:key name="attrenum" match="type" use="@t" />
<xsl:template match="/root/parent">
<xsl:for-each select="table">
<xsl:variable name="tab">
<xsl:value-of select="@name"/>
</xsl:variable>
<xsl:if test="$tab= 'two'"> <-- I added a name to the table with "one" "two" ..
<xsl:variable name="attribute">
<xsl:value-of select="@name"/>
</xsl:variable>
<xsl:for-each select="child">
<xsl:for-each select="type[count(. | key('attrenum', @t)[1]) = 1]">
<xsl:value-of select="@t" /> <br/>
</xsl:for-each>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</xsl:template>
But it fails if the table being searched is deep.
|
|

August 1st, 2012, 09:32 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
If your only reason for not moving to XSLT 2.0 is that you think it might be tricky, then make the move. Any time it takes will be repaid amply within a couple of weeks.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|

August 2nd, 2012, 09:06 AM
|
|
Registered User
|
|
Join Date: Jul 2012
Posts: 9
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
I gave some thoughts to the challenges arriving out of XSLT 1.0 for this problem and eventually made up my mind to move on to XSLT 2.0. With some web search I found out that xsl:for-each-group or distinct-values() could be an alternative, do you have any other better options to suggest?
for-each-group:
<xsl:for-each-group select="//type" group-by="@t">
<xsl:copy-of select="current-group( )[1]"/>
</xsl:for-each-group>
distinct-values():
<xsl:for-each select="distinct-values(type/@t)">
<xsl:sort/>
<xsl:value-of select="."/> <xsl:call-template name="newline"/>
</xsl:for-each>
But I think both are flawed, anything needs to be corrected in the above statements?
Thanks
|
|

August 2nd, 2012, 09:13 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
As far as I can see you haven´t really explained what result you want. Your attempt with for-each-group looks fine to group "type" elements by the "t" attribute value and to extract the first item in each group, although doing
Code:
<xsl:copy-of select="."/>
suffices instead of the current-group()[1] you have.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|
The Following User Says Thank You to Martin Honnen For This Useful Post:
|
|
|

August 2nd, 2012, 02:26 PM
|
|
Registered User
|
|
Join Date: Jul 2012
Posts: 9
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Thanks Martin for the response, my intention is to identify first occurrence of @t attribute in the group, but it has given an empty output, the XML which I mentioned out here is little simple and is just a prototype. The XSL used here is applied on a complex XML file which has got ~250 "type" elements something similar to:
Code:
<root>
<parent>
<table>
<attr ......>
<type t="enumone">
<info>
<name .....>
</info>
</attr>
<attr>
<type t="int">
<range min="1" max="255"/>
</type>
<info>
<name .....>
</info>
</attr>
<attr>
<type t="string">
<info>
<name .....>
</info>
</attr>
<attr ......>
<type t="enumtwo">
<info>
<name .....>
</info>
</attr>
<attr>
<type t="float">
<range min="1.0" max="25.5"/>
</type>
<info>
<name .....>
</info>
</attr>
<attr>
<type t="int">
<info>
<name .....>
</info>
</attr>
<attr>
<type t="enumone">
<info>
<name .....>
</info>
</attr>
<attr>
<type t="enumthree">
<info>
<name .....>
</info>
</attr>
<attr>
<type t="enumone">
<info>
<name .....>
</info>
</attr>
</parent>
</root>
|
|

August 3rd, 2012, 04:59 AM
|
|
Registered User
|
|
Join Date: Jul 2012
Posts: 9
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
The expected output is:
enumone
int
string
enumtwo
float
enumthree
|
|
 |