XSLT: Sort and create list with unique entries

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • XSLT: Sort and create list with unique entries

    Hi all,

    I have got the following issue, which I do not get resolved:
    I have a source XML document containing the following structure (just an extract):

    Quellcode

    1. <root>
    2. <records>
    3. <record>
    4. <attribA>A</attribA>
    5. <attribB>A</attribB>
    6. <attribC>A</attribC>
    7. <attribD>A</attribD>
    8. <attribE>A</attribE>
    9. <user>
    10. <id>UserA</id>
    11. <roles>
    12. <role>
    13. <system>
    14. <sysname>ABC</sysname>
    15. <sysattr1>A</sysattr1>
    16. <sysattr2>B</sysattr2>
    17. <sysattr3>C</sysattr3>
    18. </system>
    19. </role>
    20. <role>
    21. <system>
    22. <sysname>ABC</sysname>
    23. <sysattr1>A</sysattr1>
    24. <sysattr2>B</sysattr2>
    25. <sysattr3>C</sysattr3>
    26. </system>
    27. </role>
    28. <role>
    29. <system>
    30. <sysname>XYZ</sysname>
    31. <sysattr1>X</sysattr1>
    32. <sysattr2>Y</sysattr2>
    33. <sysattr3>Z</sysattr3>
    34. </system>
    35. </role>
    36. <role>
    37. <system>
    38. <sysname>DEF</sysname>
    39. <sysattr1>D</sysattr1>
    40. <sysattr2>E</sysattr2>
    41. <sysattr3>F</sysattr3>
    42. </system>
    43. </role>
    44. </roles>
    45. </user>
    46. <user>
    47. <id>UserB</id>
    48. <roles>
    49. <role>
    50. <system>
    51. <sysname>ABC</sysname>
    52. <sysattr1>A</sysattr1>
    53. <sysattr2>B</sysattr2>
    54. <sysattr3>C</sysattr3>
    55. </system>
    56. <system>
    57. <sysname>XYZ</sysname>
    58. <sysattr1>X</sysattr1>
    59. <sysattr2>Y</sysattr2>
    60. <sysattr3>Z</sysattr3>
    61. </system>
    62. </role>
    63. <role>
    64. <system>
    65. <sysname>DEF</sysname>
    66. <sysattr1>D</sysattr1>
    67. <sysattr2>E</sysattr2>
    68. <sysattr3>F</sysattr3>
    69. </system>
    70. </role>
    71. </roles>
    72. </user>
    73. </record>
    74. </records>
    75. </root>
    Alles anzeigen



    What I need to have is a unique node list, containing all systems with the same name exactly once. Therefore I created a for-each loop as follows:

    Quellcode

    1. <xsl:template match="/root/records/record">
    2. <xsl:for-each select="user/roles/role/system">
    3. <xsl:sort select="sysname" />
    4. <!-- Problem is, that I now get the following node list concerning "sysname":
    5. ABC
    6. ABC
    7. ABC
    8. XYZ
    9. XYZ
    10. DEF
    11. DEF
    12. But to continue with this information in another section, I want to skip
    13. the duplicates. Therefore I wanted to compare the current element with the
    14. previous one. Using preceding-sibling, following-sibling, etc. does not work.
    15. -->
    16. </xsl:for-each>
    17. </xsl:template>
    Alles anzeigen


    So my question is: How can I reduce the list to only have each entry seperatly and
    still access to all tag values below <system>

    I tried several things using preceding-sibling, etc. but it does not work. Also after sorting I could remeber the last one, but since I cannot change the value of a defined valriable, this does not work. So basically the question is when I loop through the structure ... how can I skip a a "node" which was already used in the previous loop step.

    Your help is highly appreciated !!!

    Thanks in advance,
    Steffen
  • Am einfachsten geht das mit XPath.
    Der folgende Eintrag sollte dir eine Liste aller sysnames geben:

    Quellcode

    1. //sysname[not (. = preceding::sysname)]


    Dabei holt er sich alle sysnames die in keinem vorherigen Knoten in Dokument-Order vorkommen.

    -----

    The easiest thing is to use XPath. The code mentioned above returns all sysname elements that are unique.
    The expression queries all sysnames except the ones that occured in any preceding node (in document order).

    regards
  • Hi,

    basically this seems to work fine. I will test it within the complete structure in my real scenario. One more question: Since I am in a template matching all /root/records/record ... elements do I only get the list of sysnames ´belonging to the current context? Because this would be a requirement here.

    Thank you,
    Best Regards
  • sorry for the late reply. I already thought about the variable method. But the problem was, that as soon as a variable got created, I cannot change the value anymore. The only thing I want to achieve is to check, wheter the current node/element/value within the "xsl:for-each" block is the same one as the last one (after the nodes have been sorted). Therefore I wanted to store the current one in a variable and compare it during the next loop. Not XSL but how I would do it in any other programming language:

    lastValue=""
    for each <value> in <array>
    if (<lastValue> == <value>) continue;
    lastValue=<value>
    doWhatYouWantToDo
    next

    I was not able to achieve this within XSL.

    The method using "preceding" basically works but I also have to pay attention to the fact, that the list is not sorted in the XML. Concerning the sample from the intial question, I would get a list like:

    ABC
    ABC
    XYZ
    DEF
    ABC
    XYZ
    DEF

    After sorting I would get:

    ABC
    ABC
    ABC
    DEF
    DEF
    XYZ
    XYZ

    This has to be reduced to:

    ABC
    DEF
    XYZ

    still thinking of the context (based on the <record> tag).

    Any additional comment / input would be highly appreciated espacially concerning the variable part. So your suggestion on collecting the values in a variable and query for them ... how would you handle that?

    Thanky you!