?
Solved

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

Posted on 2013-01-09
13
Medium Priority
?
231 Views
Last Modified: 2013-01-25
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

0
Comment
Question by:Molko
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 6
13 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 38760924
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

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 38760937
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
0
 

Author Comment

by:Molko
ID: 38760987
Thankyou, looks like a good starting point....
0
Interactive Way of Training for the AWS CSA Exam

An interactive way of learning that will help you visualize core concepts so that you can be more effective when taking your AWS certification exam.  Built for students by a student to help them understand the concepts that they are being taught.

 

Author Comment

by:Molko
ID: 38762414
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
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 38762434
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
0
 

Author Comment

by:Molko
ID: 38762612
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.
0
 

Author Comment

by:Molko
ID: 38762622
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
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 38762633
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
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 38762636
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
0
 

Author Comment

by:Molko
ID: 38764019
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
0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 2000 total points
ID: 38764212
You don't need a template for Adress, you need one for street

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

if you want this for all Street

If you only want this to happen for Home Streets

<xsl:template match="Address/Home/Street">
     <NEW-street>
          <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
...
0
 

Author Comment

by:Molko
ID: 38819943
Many many thanks, that really was a great help/starting solution that helped me solve the problem.

Thanks

M
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 38820199
welcome
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Browsing the questions asked to the Experts of this forum, you will be amazed to see how many times people are headaching about monster regular expressions (regex) to select that specific part of some HTML or XML file they want to extract. The examp…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
In this video we outline the Physical Segments view of NetCrunch network monitor. By following this brief how-to video, you will be able to learn how NetCrunch visualizes your network, how granular is the information collected, as well as where to f…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Suggested Courses

752 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question