Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT select distinct values using attributes

i'm trying to transform a list to a distinct values list using XSLT.

Input:

<object name="obj1"/>
<object name="obj2"/>
<object name="obj1"/>

Desired output:

<object>obj1</object>
<object>obj2</object>

Somebody an idea how to get it done either in XSLT 1.0 or 2.0?

THX

like image 623
toppless Avatar asked Oct 10 '13 10:10

toppless


People also ask

How do I find unique values in XSLT?

The fn:distinct-values function returns a sequence of unique atomic values from $arg . Values are compared based on their typed value. Values of different numeric types may be equal, for example the xs:integer value 1 is equal to the xs:decimal value 1.0, so the function only returns one of these values.

What is attribute in XSLT?

The <xsl:attribute> element creates an attribute in the output document, using any values that can be accessed from the stylesheet. The element must be defined before any other output document element inside the output document element for which it establishes attribute values.

What is Number () in XSLT?

Specifies the format pattern. Here are some of the characters used in the formatting pattern: 0 (Digit)

What is text () in XSLT?

Definition and Usage. The <xsl:text> element is used to write literal text to the output. Tip: This element may contain literal text, entity references, and #PCDATA.


1 Answers

For XSLT 1.0

objects.xml:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="objects.xsl"?>
<objects>
  <object id="id1" name="obj1"/>
  <object id="id2" name="obj2"/>
  <object id="id3" name="obj1"/>
</objects>

objects.xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:key name="index1" match="*" use="@name" />

    <xsl:template match="/">
        <objects>
        <xsl:for-each select="//*[generate-id() = generate-id(key('index1',@name)[1])]">
            <object><xsl:value-of select="@name"/></object>
        </xsl:for-each>
        </objects>
    </xsl:template>

</xsl:stylesheet>

What is happening here:

  1. With <xsl:key name="index1" match="*" use="@name" /> you define an index for key() function named index1. It must be outside of xsl:template declaration.
  2. With match="*" you define that it is suitable for all elements.
  3. With use="@name" you define a search criteria for index1.
  4. Now key("index1","obj1") will return an array consists of nodes where attribute @name equals "obj1": [<object name="obj1" id="id1"/>,<object name="obj1" id="id3"/>].
  5. You will need generate-id() function that generates unique ID for a given node.
  6. Called with a parameter, generate-id(<object name="obj1" id="id1"/>) will return something like "id0xfffffffff6ddca80obj1".
  7. Called without parameters, generate-id() will return an ID for a current node.
  8. You start xsl:for-each cycle for all elements //* with condition that generate-id() of current node must be equal to generate-id() of a first node from key('index1',@name) result. Which means it must be the first node itself.
  9. You print current @name value with xsl:value-of. Since it happens only for a first element of key('index1',@name) result, it will be printed only once.
like image 83
user619271 Avatar answered Oct 16 '22 09:10

user619271