Link to home
Start Free TrialLog in
Avatar of Molko
Molko

asked on

XSLT - Transform one XML document to another "very similar" structure

Hi

I have a feeling this is very straightforward becuase the source and destination documents are so similar.

I'm looking for an XSLT transform that will do work, i've been working on a few but have scrapped them because the were so verbose....i feel there is an elegant solution that is just escaping me. So any help would really be appreciated...

Ive shown the XML below, the subtle differences are in the way the numbers and the address have been separated out and also the introduction of an xxxComment element which can just have any random value in at the moment.

many  thanks for any help

Source :

<?xml version="1.0" encoding="UTF-8"?>
<Customers>
	<Type>
		<Name>Purchases</Name>
		<Date>June</Date>
		<id>100</id>
	</Type>
	<Detail>
		<Name>
			<FirstName>Gary</FirstName>
			<LastName>Smith</LastName>
		</Name>
		<Number>
			<Mobile>07712312313</Mobile>
		</Number>
		<Address>
			<Home>
				<Number>12</Number>
				<Street>Fleet Street</Street>
				<County>This county</County>
				<Postcode>A Postcode</Postcode>
				<Country>UK</Country>
			</Home>
		</Address>
	</Detail>
	<Detail>
		<Name>
			<FirstName>Mary</FirstName>
			<LastName>Jones</LastName>
		</Name>
		<Number>
			<Mobile>123456</Mobile>
			<Home>67890</Home>
			<Work>145678</Work>
		</Number>
		<Address>
			<Home>
				<Number>22</Number>
				<Street>Fleet Street</Street>
				<County>This county</County>
				<Postcode>A Postcode</Postcode>
				<Country>UK</Country>
			</Home>
		</Address>
	</Detail>
</Customers>

Open in new window


Destination

<?xml version="1.0" encoding="UTF-8"?>
<Customers>
	<Type>
		<Name>Purchases</Name>
		<Date>June</Date>
		<id>100</id>
	</Type>
	<Detail>
		<CustomerComment value="abc" type="1"/>
		<Name>
			<NameComment value="abc" type="1"/>
			<FirstName>Gary</FirstName>
			<LastName>Smith</LastName>
		</Name>
		<Number>
			<NumberComment value="abc" type="1"/>
			<Number>07712312313</Number>
			<Type>Mobile</Type>
		</Number>
		<Address>
			<AddressComment value="abc" type="1"/>
			<Home>
				<Number>12</Number>
				<Street>Fleet Street</Street>
				<County>This county</County>
				<Postcode>A Postcode</Postcode>
				<Country>UK</Country>
			</Home>
		</Address>
	</Detail>
	<Detail>
		<CustomerComment value="abc" type="2"/>
		<Name>
			<NameComment value="abc" type="2"/>
			<FirstName>Mary</FirstName>
			<LastName>Jones</LastName>
		</Name>
		<Number>
			<Number>
				<NumberComment value="abc" type="2"/>
				<Number>123456</Number>
				<Type>Mobile</Type>
			</Number>
			<Number>
				<NumberComment value="abc" type="2"/>
				<Number>67890</Number>
				<Type>Home</Type>
			</Number>
			<Number>
				<NumberComment value="abc" type="2"/>
				<Number>145678</Number>
				<Type>Work</Type>
			</Number>
		</Number>
		<Address>
			<AddressComment value="abc" type="2"/>
			<Type>Home</Type>
			<Number>22</Number>
			<Street>Fleet Street</Street>
			<County>This county</County>
			<Postcode>A Postcode</Postcode>
			<Country>UK</Country>
		</Address>
		<Address> <!-- If there was a work address-->
			<AddressComment value="abc" type="2"/>
			<Type>Work</Type>
			<Number>22</Number>
			<Street>Fleet Street</Street>
			<County>This county</County>
			<Postcode>A Postcode</Postcode>
			<Country>UK</Country>
		</Address>
	</Detail>
</Customers>

Open in new window

Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

You need an identity copy with some templates added for specilisations

This will get you started

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    
    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>
    
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Detail">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <CustomerComment value="abc">
                <xsl:attribute name="type">
                    <xsl:number count="Detail"/>
                </xsl:attribute>
            </CustomerComment>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Detail/Number/*">
        <Number>
            <CustomerComment value="abc">
                <xsl:attribute name="type">
                    <xsl:number count="Detail"/>
                </xsl:attribute>
            </CustomerComment>
            <Number><xsl:value-of select="."/></Number>
            <Type><xsl:value-of select="name()"/></Type>
        </Number>
    </xsl:template>
    
 

Open in new window

The processing of the first and second Detail are not consistent in your result description. But I think the above can get you started anyway
Avatar of Molko
Molko

ASKER

Thankyou, looks like a good starting point....
Avatar of Molko

ASKER

Hi

Thanks, that was a good starting point and has got me on my way, I can see your solution - thankyou.

I have another related question (I actually made an error in my original question), I would like the output be modified as such

<NEW_Customer id ="100"> rather than <Customers>

So it would look like


<NEW_Customer id=100>
	<Type>
.....
....
        </NEW_Customers>

Open in new window


I dont know how to intercept your initial copy-of statement and replace "Customers" with NEW_Customer and also add the attribute

Thanks in advance
Each element that is not captured by a specific template, will be captured by this one

   <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>
 
If you add a template for a specific element, the above template will no longer hold for that specific element

So by simply adding something like this
 
  <xsl:template match="/Customers">
        <NEW_Customer id="100">
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </NEW_Customer>

you will get what you need
Avatar of Molko

ASKER

Thanks, I did try something similar prior to you response as I figured that was the way go.

It does work, but.....actually XML record starts like

<?xml version="1.0" encoding="UTF-8"?>
<DATA xmlns="urn:DATA " xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:data="urn:DATA " xmlns:xs="http://www.w3.org/2001/XMLSchema">
<Customers>
.....
....
</Customers>

Open in new window


Which mean the transform does not appear to work, however if i remove the above and just have the raw XML (as i originally supplied) then is appears to work

So i am not really sure what is going on.
Avatar of Molko

ASKER

To clarify

With the namspace declarations, it appears the original template is not matching on anything specific,  so it just ends up copy the original document verbatim

If i remove the namespace declarations then is works

How do i accomodate the namespace
aha, I assume you get an exact copy of the source XML?

There is a namespace involved xmlns="urn:DATA
which means that by default all elements are in that namespace

Here is what you need to do

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns="urn:DATA "
     xmlns:dta="urn:DATA "
    version="1.0">

Now all nodes in the result tree will be in the same namespace
and all elements that get a dta: prefix will be understood to be from your source XML

This means that you need to change all templates

eg.

<xsl:template match="dta:Detail">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <CustomerComment value="abc">
                <xsl:attribute name="type">
                    <xsl:number count="dta:Detail"/>
                </xsl:attribute>
            </CustomerComment>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

note the added dta:
you need to do that for all templates you have
OK, I was writing so I missed your last message.
That one just confirms what I thought, so happy to make the right analysis, means that the proposed solution is right too
Avatar of Molko

ASKER

Thanks, the namespace qualification did trick....thankyou

I have one last question if you dont mind :-)

I understand the logic flow, in that each element that is not processed by specific template, will be captured by :

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

And it works great :-)

How would i modify sub element so that, for example

--- source --
            <Address>
                  <Home>
                        <Number>12</Number>
                        <Street>Fleet Street</Street>
                        <County>This county</County>
                        <Postcode>A Postcode</Postcode>
                        <Country>UK</Country>
                  </Home>
            </Address>

--- destination ---

            <Address>
                  <Home>
                        <Number>12</Number>
                        <NEW-Street>Fleet Street</Street>
                        <County>This county</County>
                        <Postcode>A Postcode</Postcode>
                        <Country>UK</Country>
                  </Home>
            </Address>


I guess i would need a new template to catch the Address, but how would I transform 'Street' to 'NEW-Street', would i need a futher template that  matches on 'Address/Street' ?

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

Thanks
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Molko

ASKER

Many many thanks, that really was a great help/starting solution that helped me solve the problem.

Thanks

M