Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old March 4th, 2010, 05:33 AM
Registered User
 
Join Date: Mar 2010
Posts: 2
Thanks: 3
Thanked 0 Times in 0 Posts
Default XLST noob - XML -> XML transform help required

Just had this dropped on me and I have almost zero experience with XLST.
I need to convert the following XML...

<Outer>
<A>Contains lots of child nodes</A>
<A>Contains lots of child nodes</A>
<A>Contains lots of child nodes</A>
<!--- Repititions of the above nodes. not fixed in number -->
<MainNode>
Contains lots of child nodes
</Mainnode>
<!--- Repititions of all the above nodes. not fixed in number -->
</Outer>

Convert to.....

<Outer>
<MainNode>
Contains lots of child nodes
<A>Contains lots of child nodes</A>
<A>Contains lots of child nodes</A>
<A>Contains lots of child nodes</A>
<!--- Repititions of the above nodes. Not fixed in number -->
</Mainnode>
<!--- Repititions of all the above nodes. Not fixed in number -->
</Outer>

Basically, I want to move all the <A> nodes that precede each <MainNode>, inside the <Mainnode> as children nodes of it. There can be many <A> nodes for each <MainNode> of which there may also be many.

As I say I am virtually a complete newbie to XSLT but do not wish to be spoonfed and answer as I won't learn anything from it. Psuedocode, hints of the keywords or useful links would be much appreciated though.
I know I am going to be using xsl:For-each, xsl:copy-of, etc but do not know how to structure this.

Thanks
 
Old March 4th, 2010, 05:45 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

>Basically, I want to move all the <A> nodes that precede each <MainNode>, inside the <Mainnode> as children nodes of it.

That sort of hints that there might be <A> nodes that don't precede the MainNode, though your example doesn't show any.

Anyway, there are basically two rules here:

(a) if you find an A node before a MainNode, drop it

Code:
<xsl:template match="A[following-sibling::MainNode]"/>
(b) if you find a MainNode, copy its contents followed by all the preceding A nodes

[code]
<xsl:template match="MainNode">
<MainNode>
<xsl:copy-of select="child::node()"/>
<xsl:copy-of select="preceding-sibling::A"/>
</MainNode>
</xsl:template>
[code]

>I know I am going to be using xsl:For-each,

No: try to avoid xsl:for-each. Try to structure your code in terms of rules: "why I hit an XXX element, what should I do with it?"
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old March 4th, 2010, 06:02 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
Default

Assuming that the <A> and <MainNode> elements keep repeating then you need to identify <A> elements that precede a <MainNode>, but stop at the last <MainNode>

To get all the preceding <A> elements you can use the following XPATH: preceding-sibling::A

This will however not stop at the previous <MainNode> element. One way to solve this would be to check the next <MainNode> after each <A> element and ensure it is still the current <MainNode>. You can do this using the generate-id() function.

preceding-sibling::A[generate-id(following-sibling::MainNode[1])=generate-id(current())]

The complete XSLT would look something like follows.

Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output indent="yes"/>

   <xsl:template match="Outer">
           <A>
              <xsl:apply-templates select="MainNode"/>
           </A>
   </xsl:template>
   
   <xsl:template match="MainNode">
       <MainNode>
               <xsl:apply-templates select="preceding-sibling::A[generate-id(following-sibling::MainNode[1])=generate-id(current())]"/>
           </MainNode>
       </xsl:template>
       
       <xsl:template match="A">
            <A1><xsl:apply-templates /></A1>
        </xsl:template>
 
</xsl:stylesheet>
__________________
/- Sam Judson : Wrox Technical Editor -/

Think before you post: What have you tried?
 
Old March 4th, 2010, 07:09 AM
Registered User
 
Join Date: Mar 2010
Posts: 2
Thanks: 3
Thanked 0 Times in 0 Posts
Default

Ok, guys, that has helped me to get started. thank you very much.
Now can I ask if this is possible.... it's like an opposite of the previous question. As a newbie to this stuff, I think my gaffers are trying to confoose me!!!

XML source
<a>
<b>
<b1></b1>
<b2></b2>
<b3></b3>
<c>
<c1></c1>
<c2></c2>
</c>
Multiple c-nodes here
</b>
</a>

XML Target
<a>
<c>
<c1></c1>
<c2></c2>
</c>
multiple c-nodes here
<b>
<b1></b1>
<b2></b2>
<b3></b3>
</b>
</a>
 
Old March 4th, 2010, 07:26 AM
Friend of Wrox
 
Join Date: Jun 2008
Posts: 291
Thanks: 9
Thanked 29 Times in 29 Posts
Thumbs up

try the below:
Code:
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="b/c"/>
<xsl:apply-templates select="b"/>
</xsl:copy>
</xsl:template>
<xsl:template match="c">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<xsl:copy-of select="* except c"/>
</xsl:copy>
</xsl:template>
__________________
Rummy
The Following User Says Thank You to mrame For This Useful Post:
Crass1968 (March 4th, 2010)
 
Old March 4th, 2010, 07:27 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

You want a rule for <a> that's along the lines

Code:
<xsl:template match="a">
  <a>
     <xsl:apply-templates select="b/c"/>
     <xsl:apply-templates select="b"/>
  </a>
</xsl:template>
and a template rule for <b>

Code:
<xsl:template match="b">
<b>
  <xsl:apply-templates select="child::node()[not(self::c)]"/>
</b>
</xsl:template>
and everything else can use the identity template

Code:
<xsl:template match="*">
  <xsl:copy>
     <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
The Following User Says Thank You to mhkay For This Useful Post:
Crass1968 (March 4th, 2010)
 
Old March 4th, 2010, 07:32 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
Default

I think it would be best if you spent a little time trying to learn XSLT. I realise that it has got a bit of a learning curve, but once you have got the basics it will help you.

I'd recommend a site such as http://www.w3schools.com/xsl/

Basically you need to write a template to match each element you want to process, and within that template tell it what you want to appear in there.

There is also something called the 'identity template' which will copy all element you don't explicitly call out in other templates.

Code:
<!-- match the 'a' element -->
<xsl:template match="a">
  <!-- copy the 'a' element -->
  <xsl:copy>
  <!-- process the 'b' elements -->
   <xsl:apply-templates select="b"/>
  <!-- then process the 'c' elements -->
   <xsl:apply-templates select="b/c"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="b">
  <xsl:copy>
   <!-- process all inner elements apart from those called 'c' -->
   <xsl:apply-templates select="*[local-name()!='c']"/>
  </xsl:copy>
</xsl:template>

<!-- Identity Template -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
__________________
/- Sam Judson : Wrox Technical Editor -/

Think before you post: What have you tried?
The Following User Says Thank You to samjudson For This Useful Post:
Crass1968 (March 4th, 2010)





Similar Threads
Thread Thread Starter Forum Replies Last Post
<?javax.xml.transform.disable-output-escaping ?> robbert XSLT 5 January 5th, 2011 07:28 PM
Transform xml to xml changing one tag. surfer97301 XSLT 2 April 21st, 2010 05:14 PM
Split xml file with result document and javax.xml.transform.Transformer. nisargmca XSLT 3 January 12th, 2010 06:26 AM
How do I transform XSLT without the <?xml?> tag? nadavvin XSLT 4 June 10th, 2007 09:25 AM
XSLT read through XML to transform another XML dendenx2 XSLT 8 July 7th, 2005 08:18 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.