 |
| 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
|
|
|
|

February 20th, 2011, 03:30 AM
|
|
Registered User
|
|
Join Date: Feb 2011
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Convert nested XML to flat
Hello,
I am new to XSLT and was wondering if someone could guide me on how to do the following conversion. I basically need nodes PT1, PT2, PT3 (with their immediate children) to appear at the same level as PTA
Source XML
Code:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>00011</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>00011</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>00011</CTRLNUM>
</PT3>
</PT1>
</PTA>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
to
Target XML
Code:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>00011</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>00011</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>00011</CTRLNUM>
</PT3>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
Thank you for any guidance
neel
|
|

February 20th, 2011, 04:28 AM
|
|
|
Parental Control
|
|

February 20th, 2011, 11:27 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Here is an XSLT 1.0 stylesheet that should do the job:
Code:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[PTA]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="PTA"/>
<xsl:apply-templates select="PTA/PT1 | PTA//PT2 | PTA//PT3"/>
<xsl:apply-templates select="PTA/following-sibling::*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PTA">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT1)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PTA/PT1">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT2 or self::PT3)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|
The Following User Says Thank You to Martin Honnen For This Useful Post:
|
neelP (February 20th, 2011)
|
|

February 20th, 2011, 02:24 PM
|
|
Registered User
|
|
Join Date: Feb 2011
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Thank you Martin - that was perfect. I even created multiple instances of the PT1,PT2 and PT3 nodes under PTA and it still worked and maintained the sequence. Is there a way to handle multiple PTAs under the root - when I ran this against multiple PTAs it did not quite work the way I wanted it to (of course my mistake for not mentioning it as a possibility in my original post)
I was hoping to see
PTA
PT1
PT2
PT3
PTA
PT1
PT2
PT3
but got
PTA
PTA
PT1
PT2
PT3
PT1
PT2
PT3
PTA (an additional instance at end)
The requirement to maintain the sequence is because I am feeding this to a file adapter that takes the XML (cannot be nested) and writes out a file
Thanks again
neel
|
|

February 20th, 2011, 02:36 PM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Let's restructure the code:
Code:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PTA">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT1)]"/>
</xsl:copy>
<xsl:apply-templates select="PT1 | PT1//PT2 | PT1//PT3"/>
</xsl:template>
<xsl:template match="PTA/PT1">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT2 or self::PT3)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Untested but should hopefully do what you want. If not then please post a new complete representative XML input sample and the result you want for that sample.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|

February 20th, 2011, 03:24 PM
|
|
Registered User
|
|
Join Date: Feb 2011
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Martin,
Dont know why but when I ran it against the new restructured XSLT, I got only the PTA nodes - the other nodes did not output
The sample input I am testing against is (CTRLNUM has the sequence)
Code:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0002</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0003</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0004</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0005</CTRLNUM>
</PT3>
</PT1>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0006</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0007</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0008</CTRLNUM>
</PT3>
</PT1>
</PTA>
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00009</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0010</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0011</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0012</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0013</CTRLNUM>
</PT3>
</PT1>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0014</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0015</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0016</CTRLNUM>
</PT3>
</PT1>
</PTA>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
The output I am hoping to get is
Code:
<?xml version="1.0" encoding="utf-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0002</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0003</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0004</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0005</CTRLNUM>
</PT3>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0006</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0007</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0008</CTRLNUM>
</PT3>
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00009</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0010</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0011</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0012</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0013</CTRLNUM>
</PT3>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0014</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0015</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0016</CTRLNUM>
</PT3>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
Thank you
neel
|
|

February 21st, 2011, 02:51 AM
|
|
Registered User
|
|
Join Date: Feb 2011
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
|
|
Since I had to meet a deadline, I cheated a little. I have some control over the input XML so I introduced a PTAParent node just above the repeating PTA nodes. I did this because your XSLT seems to handle the inner repeating nodes (PR1,PT2,PT3) just fine - it failed only when PTA repeated.
So its
Code:
PTAParent
PTA
PT1
PT2
PT3
PT1
PT2
PT3
PTA
PT1
PT2
PT3
EOF
I tweaked the XSLT you provided and this seems to be working now - thanks for your help
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[PTAParent]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="PTAParent"/>
<xsl:apply-templates select="PTA"/>
<xsl:apply-templates select="PTAParent/PTA | PTAParent//PT1 | PTAParent//PT2 | PTAParent//PT3 "/>
<xsl:apply-templates select="PTAParent/following-sibling::*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PTAParent">
</xsl:template>
<xsl:template match="PTA">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT1)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PTA/PT1">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT2 or self::PT3)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
neel
|
|

February 21st, 2011, 10:36 AM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Quote:
Originally Posted by neelP
Martin,
Dont know why but when I ran it against the new restructured XSLT, I got only the PTA nodes - the other nodes did not output
The sample input I am testing against is (CTRLNUM has the sequence)
Code:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0002</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0003</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0004</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0005</CTRLNUM>
</PT3>
</PT1>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0006</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0007</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0008</CTRLNUM>
</PT3>
</PT1>
</PTA>
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00009</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0010</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0011</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0012</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0013</CTRLNUM>
</PT3>
</PT1>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0014</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0015</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0016</CTRLNUM>
</PT3>
</PT1>
</PTA>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
The output I am hoping to get is
Code:
<?xml version="1.0" encoding="utf-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0002</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0003</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0004</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0005</CTRLNUM>
</PT3>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0006</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0007</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0008</CTRLNUM>
</PT3>
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00009</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0010</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0011</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0012</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0013</CTRLNUM>
</PT3>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0014</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0015</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0016</CTRLNUM>
</PT3>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
Thank you
neel
|
I am not sure why you don't get the result you want, when I run the stylesheet
Code:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PTA">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT1)]"/>
</xsl:copy>
<xsl:apply-templates select="PT1 | PT1//PT2 | PT1//PT3"/>
</xsl:template>
<xsl:template match="PTA/PT1">
<xsl:copy>
<xsl:apply-templates select="@* | *[not(self::PT2 or self::PT3)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
against the XML input
Code:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0002</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0003</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0004</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0005</CTRLNUM>
</PT3>
</PT1>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0006</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0007</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0008</CTRLNUM>
</PT3>
</PT1>
</PTA>
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00009</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0010</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0011</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0012</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0013</CTRLNUM>
</PT3>
</PT1>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0014</CTRLNUM>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0015</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0016</CTRLNUM>
</PT3>
</PT1>
</PTA>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
with Saxon 6.5.5 I get the following result:
Code:
<?xml version="1.0" encoding="utf-8"?>
<ns0:RTP xmlns:ns0="urn:Test_Area">
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00001</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0002</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0003</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0004</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0005</CTRLNUM>
</PT3>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0006</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0007</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0008</CTRLNUM>
</PT3>
<PTA>
<RECID>PTA</RECID>
<CTRLNUM>00009</CTRLNUM>
<PIIN>748159-26-3-3</PIIN>
</PTA>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0010</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0011</CTRLNUM>
</PT2>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0012</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0013</CTRLNUM>
</PT3>
<PT1>
<RECID>PT1</RECID>
<CTRLNUM>0014</CTRLNUM>
</PT1>
<PT2>
<RECID>PT2</RECID>
<CTRLNUM>0015</CTRLNUM>
</PT2>
<PT3>
<RECID>PT3</RECID>
<CTRLNUM>0016</CTRLNUM>
</PT3>
<EOF>
<RECID>EOF</RECID>
<VERS>700</VERS>
<COUNT>2</COUNT>
</EOF>
</ns0:RTP>
So that has the elements you asked for.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
Last edited by Martin Honnen; February 21st, 2011 at 10:38 AM..
Reason: correcting copy/paste error
|
|
 |