Reputation: 247
Hi if I have the following XML input file
<data>
<group id= "1">
<phrase>Doc1</phrase>
<document refid ="3"/>
<document refid ="5"/>
<document refid= "1"/>
</group>
<group id= "2">
<phrase>Doc2</phrase>
<document refid ="2"/>
<document refid ="3"/>
<document refid= "6"/>
</group>
<group id= "3">
<phrase>Doc3</phrase>
<document refid ="2"/>
<document refid ="3"/>
<document refid= "4"/>
</group>
</data>
Is it possible to have an output which checks each group to see if the document "refid" number was not displayed in the previous groups? For example I would like my output as
<data>
<group id= "1">
<phrase>Doc1</phrase>
<document refid ="3"/>
<document refid ="5"/>
<document refid= "1"/>
</group>
<group id= "2">
<phrase>Doc2</phrase>
<document refid ="2"/>
<document refid= "6"/>
</group>
<group id= "3">
<phrase>Doc3</phrase>
<document refid= "4"/>
</group>
</data>
I am trying to do this in XSLT 1.0. I hope this explains the question clearly. I would really appreciate your help. Thank you
Upvotes: 2
Views: 1170
Reputation: 243479
Here is an efficient and short solution, using keys (Muenchian grouping):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kDocById" match="document" use="@refid"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group">
<xsl:copy>
<xsl:apply-templates select=
"@*
|
node()[not(self::document)]
|
document
[generate-id()
=
generate-id(key('kDocById', @refid)[1])
]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document (reflecting the latest refinement made in a comment by the OP):
<data>
<group id= "1">
<phrase>Doc1</phrase>
<document refid ="3"/>
<document refid ="5"/>
<document refid= "1"/>
</group>
<group id= "2">
<phrase>Doc2</phrase>
<document refid ="2"/>
<document refid ="3"/>
<document refid= "6"/>
</group>
<group id= "3">
<phrase>Doc3</phrase>
<document refid ="2"/>
<document refid ="3"/>
<document refid= "4"/>
</group>
</data>
the wanted, correct result is produced:
<data>
<group id="1">
<phrase>Doc1</phrase>
<document refid="3"/>
<document refid="5"/>
<document refid="1"/>
</group>
<group id="2">
<phrase>Doc2</phrase>
<document refid="2"/>
<document refid="6"/>
</group>
<group id="3">
<phrase>Doc3</phrase>
<document refid="4"/>
</group>
</data>
Upvotes: 1
Reputation: 16232
This seems to do the trick:
$ cat style.xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!--
This templates matches the document elements with a @refid that has already been used
in on of the previous groups. They are simply ignored.
-->
<xsl:template match="document[../preceding-sibling::group/document/@refid = current()/@refid]" />
<!--
Everything else gets copied to the output.
-->
<xsl:template match="@*|*|text()">
<xsl:copy>
<xsl:apply-templates select="@*|*|text()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
$ xsltproc style.xsl input.xml
<?xml version="1.0"?>
<data>
<group id="1">
<document refid="3"/>
<document refid="5"/>
<document refid="1"/>
</group>
<group id="2">
<document refid="2"/>
<document refid="6"/>
</group>
<group id="3">
<document refid="4"/>
</group>
</data>
Edit: to incorporate it into your stylesheet, try to change the for-each
at line 49 to:
<xsl:for-each select="document[not(../preceding-sibling::group/document/@refid = current()/@refid)]">
Upvotes: 1