Mike
Mike

Reputation: 616

XSLT Adding node in the correct place

I have the following XML structure:

<Main>
    <Node1>Definite</Node1>
    <Node2>Definite</Node2>
    <Node3>Definite</Node3>
    <Node4>Definite</Node4>
    <Node5>Definite</Node5>
    <Node6>Definite</Node6>
    <A>Possible</A>
    <B>Possible</B>
    <C>Possible</C>
    <D>Possible</D>    
    <E>Possible</E>
    <F>Possible</F>
    <G>Possible</G>
    <H>Possible</H>
    <I>Possible</I>
</Main>

The nodes named individual letters eg.<A> are nodes that may not exist within the XML structure while all other nodes are definite.

I need to insert the node <ZZZ> within the structure so that it always sits in the position shown below.

<Main>
    <Node1>Value</Node1>
    <Node2>Value</Node2>
    <Node3>Value</Node3>
    <Node4>Value</Node4>
    <Node5>Value</Node5>
    <Node6>Value</Node6>
    <A>Value</A>
    <B>Value</B>
    <C>Value</C>
    <D>Value</D>    
    <E>Value</E>
    <ZZZ>Value</ZZZ>
    <F>Value</F>
    <G>Value</G>
    <H>Value</H>
    <I>Value</I>
</Main>

So say node <E> and <C> and <H> didnt exist it would be:

<Main>
    <Node1>Value</Node1>
    <Node2>Value</Node2>
    <Node3>Value</Node3>
    <Node4>Value</Node4>
    <Node5>Value</Node5>
    <Node6>Value</Node6>
    <A>Value</A>
    <B>Value</B>
    <D>Value</D>    
    <ZZZ>Value</ZZZ>
    <F>Value</F>
    <G>Value</G>
    <I>Value</I>
</Main>

Hope this is explained clear enough :)

Upvotes: 1

Views: 126

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

A more general solution that can be adapted to work with any dynamically specidfied names and any number of definite or possible names:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:occurences>
    <definites>
        <definite>Node1</definite>
        <definite>Node2</definite>
        <definite>Node3</definite>
        <definite>Node4</definite>
        <definite>Node5</definite>
        <definite>Node6</definite>
    </definites>
    <possibles>
        <possible>A</possible>
        <possible>B</possible>
        <possible>C</possible>
        <possible>D</possible>
        <possible>E</possible>
        <possible>F</possible>
        <possible>G</possible>
        <possible>H</possible>
        <possible>I</possible>
    </possibles>
 </my:occurences>

 <xsl:variable name="vOccurencies" select=
   "document('')/*/my:occurences"/>

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

 <xsl:template match="/*">
  <xsl:copy>
   <xsl:apply-templates select=
    "*[name() =
        ($vOccurencies/definites/definite
        |
         $vOccurencies/possibles/possible
                  [not(position()
                  >
                   string-length(substring-before($vOccurencies/possibles, 'F')))
                   ]
         )
       ]"/>
   <ZZZ>Value</ZZZ>
   <xsl:apply-templates select=
   "*[name()
     =
      $vOccurencies/possibles/possible
                  [position()
                  >
                   string-length(
                      substring-before($vOccurencies/possibles, 'F')
                                )
                   ]
     ]"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<Main>
    <Node1>Value</Node1>
    <Node2>Value</Node2>
    <Node3>Value</Node3>
    <Node4>Value</Node4>
    <Node5>Value</Node5>
    <Node6>Value</Node6>
    <A>Value</A>
    <B>Value</B>
    <C>Value</C>
    <D>Value</D>
    <E>Value</E>
    <ZZZ>Value</ZZZ>
    <F>Value</F>
    <G>Value</G>
    <H>Value</H>
    <I>Value</I>
</Main>

the wanted, correct result is produced:

<Main>
   <Node1>Value</Node1>
   <Node2>Value</Node2>
   <Node3>Value</Node3>
   <Node4>Value</Node4>
   <Node5>Value</Node5>
   <Node6>Value</Node6>
   <A>Value</A>
   <B>Value</B>
   <C>Value</C>
   <D>Value</D>
   <E>Value</E>
   <ZZZ>Value</ZZZ>
   <F>Value</F>
   <G>Value</G>
   <H>Value</H>
   <I>Value</I>
</Main>

Do Note:

In a realworld situation the my:occurances element will be in its separate XML document -- thus the XSLT code has no hardcoded element names and needn't be modified when the occurencies xml document is changed.

Upvotes: 0

Nico Kutscherauer
Nico Kutscherauer

Reputation: 340

well it is depending on which elements are requiered and which are optional! E.g. if you can say <F> is requierd you can insert the ZZZ-Element before the F-Element:

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

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

if you can not say, there is a requiered element, you need to insert in a template for the Main-Element:

<xsl:template match="Main">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="Node1|Node2|Node3|Node4|Node5|Node6|A|B|C|D|E"/>
        <ZZZ>Value</ZZZ>
        <xsl:apply-templates select="F|G|H|I"/>
    </xsl:copy>
</xsl:template>

Upvotes: 2

Related Questions