0

I have been trying to change the order of the nodes based on structure.

Lets assume that we have an example xml

<?xml version="1.0" encoding="UTF-8"?>
<ParentNode>
  <example>value</example>
  <example>value</example>
  <node>
     <one>1</one>
     <two>1</two>
     <three>1</three>
     <four>1</four>
  </node>
  <node>
     <one>2</one>
     <two>2</two>
     <three>2</three>
     <four>2</four>
  </node>
</ParentNode>

This <node> part is repeating for other values as well, this is the simplified version of the whole structure.

What i want is: I want to change the order of the <node> with values 2 , with <node> with values 1

 <?xml version="1.0" encoding="UTF-8"?>
<ParentNode>
  <example>value</example>
  <example>value</example>
  <node>
     <one>2</one>
     <two>2</two>
     <three>2</three>
     <four>2</four>
  </node>
  <node>
     <one>1</one>
     <two>1</two>
     <three>1</three>
     <four>1</four>
  </node>
</ParentNode>

Lets assume that, <three> is a key value for us to re-order the nodes, So i would like to say <xsl:when test="value=2"> put whole before the first one.

How can i write it in XSLT 2.0 ?

EDIT: I found the solution by changing the variables inside the templates, So What i did is, putting the value "2" nodes into "1" and, This is a manual solution, but at the end, it works. Thank you for the ideas

Sojimanatsu
  • 619
  • 11
  • 28
  • Can the `node` elements have other siblings as well e.g. `...............`? What is supposed to happen in this case, do you want to sort only adjacent `node` elements or do you want to sort all `node` children of a `ParentNode` together? – Martin Honnen Feb 12 '18 at 16:16
  • I just want to order spesific not all of them since other elements have different values, i dont want to touch them, imagine in a single file a lot of elements , but i only want to order in this two spesific case. I have to say, reach that node where value of is 2 , and reach the where is 1, and change the order of them. – Sojimanatsu Feb 12 '18 at 16:25
  • You have said what you want to happen with one particular source document, but I am having trouble extrapolating from this to the general case: what other documents might you encounter, and what are the general rules for reordering them? – Michael Kay Feb 12 '18 at 17:04
  • The thing is that I try to transform from one XML document to another XML, in the first xml the with child node , appears before in the xml 2 , it has to be the other way around. Since i use apply templates , it also applies the order as you know, but i want to write a spesific case for just this part of xml, and say reach the where and and change their order like in the example i gave. Hope i could tell. – Sojimanatsu Feb 12 '18 at 17:09

2 Answers2

2

Write two templates

  <xsl:template match="node[three = 1]">
      <xsl:copy-of select="../node[three = 2]"/>
  </xsl:template>

  <xsl:template match="node[three = 2]">
      <xsl:copy-of select="../node[three = 1]"/>
  </xsl:template>

plus the identity transformation and the two elements are swapped (XSLT 3 version at http://xsltfiddle.liberty-development.net/3Nqn5Yd, for XSLT 2 you have to spell out the identity transformation template:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

  <xsl:template match="node[three = 1]">
      <xsl:copy-of select="../node[three = 2]"/>
  </xsl:template>

  <xsl:template match="node[three = 2]">
      <xsl:copy-of select="../node[three = 1]"/>
  </xsl:template>

</xsl:transform>

http://xsltransform.hikmatu.com/gWcDMek

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
1

If you happen to have any literal order in your nodes values you could use the xsl:sort function to reorder your them:

<xsl:template match="/ParentNode">
  <xsl:copy>
    <xsl:copy-of select="example" />
    <xsl:for-each select="node">  
      <xsl:sort select="three" order="descending" />
      <xsl:copy>
        <xsl:copy-of select="node()|@*" />
      </xsl:copy>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

Output:

<ParentNode>
    <example>value</example>
    <example>value</example>
    <node>
        <one>2</one>
        <two>2</two>
        <three>2</three>
        <four>2</four>
    </node>
    <node>
        <one>1</one>
        <two>1</two>
        <three>1</three>
        <four>1</four>
    </node>
</ParentNode>
zx485
  • 28,498
  • 28
  • 50
  • 59
  • Sorry i think i didnt mention that , values are not an integer, they are strings like "example1" and "example2" so 1 and 2 are representing different string values – Sojimanatsu Feb 12 '18 at 16:53
  • @Sojimanatsu: That's ok. Just in case you need a literal order (or any other in XSLT 2.0/3.0) this answer may be of use. – zx485 Feb 12 '18 at 16:55
  • If you have XSLT-2.0 or 3.0 available you could use a function as parameter to the `xsl:sort` like described in [this SO answer](https://stackoverflow.com/a/5965324/1305969). – zx485 Feb 12 '18 at 17:01
  • But still, this algorithm orders every node i want to spesifcy just in this cases where is equal to 2 and 1 , exchange their order. Maybe i overthink about the solution – Sojimanatsu Feb 12 '18 at 17:01