• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1106
  • Last Modified:

use xslt to copy child nodes

right now i have the following:
my xml is like:

....
<NODE1>
<CHILDNODE1>
<BLAH>
<BLAH2>
<CHILDNODE2>
</CHILDNODE1>
<CHILDNODE1>
<BLAH>
<BLAH2>
<CHILDNODE2>
<CHILDNODE1>
</NODE1>
....

and then i have this xslt:

<xsl:template match="/">

<xsl:apply-templates/>
</xsl:template>

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

       


<xsl:template match="NODE1">
 
            <xsl:apply-templates select="CHILDNODE1">
                              <xsl:sort select="CHILDNODE2"/>
            </xsl:apply-templates>
                              
 </xsl:template>

which is copying the <CHILDNODE1>'S and its children... but i also want it to copy <NODE1> and its children... hwo can i do that?
0
wrynn
Asked:
wrynn
  • 6
  • 5
  • 2
  • +1
2 Solutions
 
abelCommented:
Hi Wrynn,

You can do that by completing the matching template for NODE1 and have it copy the node. The fix is to add <xsl:copy> and <xsl:copy-of select="@*" /> right under <xsl:template match="NODE1" />, see code snippet:

HTH,

Cheers,
Abel Braaksma
<xsl:template match="NODE1">
   <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates select="CHILDNODE1">
          <xsl:sort select="CHILDNODE2"/>
      </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

Open in new window

0
 
Geert BormansCommented:
this template is copying all nodes,
except the NODE1
That is because you don't have an xsl:copy in the template for NODE1

<xsl:template match="NODE1">
 
            <xsl:apply-templates select="CHILDNODE1">
                              <xsl:sort select="CHILDNODE2"/>
            </xsl:apply-templates>
                             
 </xsl:template>

should be

<xsl:template match="NODE1">
   <xsl:copy>
            <xsl:apply-templates select="CHILDNODE1">
                              <xsl:sort select="CHILDNODE2"/>
            </xsl:apply-templates>
   </xsl:copy>
 </xsl:template>
0
 
abelCommented:
The reason that it is not copying the NODE1 in your original code is that XSLT works its way through your matches for every node it encounters (the order of which is determined by the <xsl:apply-templates ...>). Then, when it finds multiple matches (i.e., the match="node()" and the match="NODE1") it will choose the most specific of the two.

If you want them *both* to match, you'll have to instruct it to do so. This is easiest in XSLT 2.0, with xsl:next-match. You can also re-apply the current node, but then the problem is one of order: where would you do it and how do you prevent eternal loops? There are ways around it, but in your situation I don't see a compelling reason to break with the traditional "most specific match" rule.

Note that you chose the best way for using XSLT: matching templates. Many users start out with using xsl:for-each because it reminds them of for...next from their other programming environments (Java, C, PHP etc). However, in XSLT, you can define rules of how your output should look like and let the processor decide the order of execution. Which is just what you did. You will find it a very powerful method (i.e.: a catch-all for many nodes, and some exception rules for some other nodes and you are done in most trivial scenarios).

Cheers,
-- Abel --
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
wrynnAuthor Commented:
these solutions are not working for me.
0
 
Geert BormansCommented:
Well, it is also not really obvious what you need.
Both abel and myself have given you the hints of where to look
Can you show a real piece of your XML (one that is at least wellformed),
a real piece of your XSLT and what you want as a result, that you are not getting

cheers

Geert
0
 
wrynnAuthor Commented:
sorry my first example was missing something that is important.  it should be

....
<NODE1>
<ANODE>
<ANODE>
<ANODE>
<CHILDNODE1>
<BLAH>
<BLAH2>
<CHILDNODE2>
</CHILDNODE1>
<ANODE>
<ANODE>
<ANODE>
<CHILDNODE1>
<BLAH>
<BLAH2>
<CHILDNODE2>
<CHILDNODE1>
</NODE1>
....

with my current xslt, it isnt printing the <NODE1> and the <ANODE>'s, but i want it to.  how can i do this?  your examples did not give me the needed solution unfortunately
0
 
Geert BormansCommented:
OK, I see,

change your template for NODE1 like this

<xsl:template match="NODE1">
   <xsl:copy>
            <xsl:apply-templates select="*[not(name() = 'CHILDNODE1']">
            <xsl:apply-templates select="CHILDNODE1">
                              <xsl:sort select="CHILDNODE2"/>
            </xsl:apply-templates>
   </xsl:copy>
 </xsl:template>

The problem with this approach is that all the ANODE elements will be outputted first
and all the CHILDNODE1 after that
since you are sorting the CHILDNODE1, I assume it is acceptable that you change some order around

cheers

Geert
0
 
Geert BormansCommented:
I need an extra ')' in this line
          <xsl:apply-templates select="*[not(name() = 'CHILDNODE1')]">
0
 
Geert BormansCommented:
XSLT2 syntax is more convenient
          <xsl:apply-templates select="node() except CHILDNODE1">
0
 
abelCommented:
Hi Wrynn,

I like to add a little to Geert's solution. It will copy the nodes as specified by the OP, but it will change the order of the nodes around CHILDNODE1. I.e., all CHILDNODE1 are the last, and the non-CHILDNODE1 nodes are the first when inside the NODE1 node.

If that is a problem, you will have to do a little more (or less) depending on your requirements. It will require a grouping methodology to do that (i.e., sort all sibling CHILDNODE1 that have CHILDNODE2 by the CHILDNODE2 contents and leave the other nodes between the grouped siblings intact).

But I am guessing here.  An alternative solution to the one provided by Geert is below (not that his isn't correct, it works just as well). Instead of guessing what you are after, it'd help us a great deal to show either the real content of your input, or a genuine example of the output you are after based on the input provided above.

In addition, please provide info on what processor you are using, like suggested by Geert, XSLT 2.0 will make for an easier solution.

Cheers,
-- Abel --
<xsl:template match="NODE1">
   <xsl:copy>
      <xsl:apply-templates select="node() | @*">
         <xsl:sort select="CHILDNODE2" />
      </xsl:apply-templates>
   </xsl:copy>
 </xsl:template>

Open in new window

0
 
Geert BormansCommented:
mmh Abel,

> and leave the other nodes between the grouped siblings intact

your code would still group all the ANODE elements before the CHILDNODE1 elements
since the sort will bring elements lacking a CHILDNODE2 child together
It has a side effect in that if any of the ANODE elements accidentaly has a CHILDNODE2,
it will end up between the CHILDNODE1 elements
making the uncontrolled grouping even worse

but all we are doing here is guessing. I think we have helped as much as we could,
as long as we don't get real XML and a real requirement

all others but abel, ignore the following...
whilst you are around the forum
I am stuck here, maybe you have an idea
http://www.experts-exchange.com/Microsoft/Development/.NET/Q_22979248.html

thanks

Geert
0
 
abelCommented:
> your code would still group all the ANODE elements before the CHILDNODE1 elements

yes, I know. I am sorry for being unclear about that, I meant to give a similar solution as yours (i.e., without specific grouping), and leave the grouping solution for if/when the OP would provide more information.

> It has a side effect in that if any of the ANODE elements accidentally has a CHILDNODE2,

thanks for pointing that out. It isn't hard to change the sort to provide for that exception, but before we continue, I agree that we should wait until the requirements get clear.

Cheers,
-- Abel --
0
 
abelCommented:
I have no objections to that
0
 
Computer101Commented:
Forced accept.

Computer101
EE Admin
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 6
  • 5
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now