|
[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] Re: Merging two xml documents using xslt
Hi Jianyu,
Please try this XSL -
Assume, this stylesheet is named as main.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="xml" indent="yes" />
<xsl:variable name="updates"
select="document('updates.xml')" />
<!-- store XPath values of all elements, of
source.xml in a variable -->
<xsl:variable name="all-xpaths">
<xsl:for-each select="//*">
<xpath>
<xsl:call-template name="constructXPathExpr">
<xsl:with-param name="node" select="." />
<xsl:with-param name="xpath"
select="name(.)" />
</xsl:call-template>
</xpath>
</xsl:for-each>
</xsl:variable>
<!-- a template rule that will match to any element
node -->
<xsl:template match="*">
<xsl:variable name="curr-node" select="." />
<xsl:variable name="xpath-expr">
<xsl:call-template name="constructXPathExpr">
<xsl:with-param name="node" select="." />
<xsl:with-param name="xpath" select="name(.)"
/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="{name()}">
<xsl:choose>
<xsl:when test="$xpath-expr =
$updates/updates/elem/@xpath">
<xsl:value-of
select="$updates/updates/elem[@xpath =
$xpath-expr]/@xvalue" />
</xsl:when>
<xsl:when test="not(*)">
<xsl:value-of select="text()" />
</xsl:when>
<xsl:otherwise>
<!-- code to create xml node in source -->
<xsl:for-each
select="$updates/updates/elem">
<xsl:if test="not(@xpath =
exsl:node-set($all-xpaths)/xpath)">
<xsl:variable name="temp" select="." />
<xsl:variable name="check">
<xsl:for-each select="$curr-node//*">
<xsl:variable name="expr">
<xsl:call-template
name="constructXPathExpr">
<xsl:with-param name="node" select="." />
<xsl:with-param name="xpath"
select="name(.)" />
</xsl:call-template>
</xsl:variable>
<xsl:if
test="starts-with($temp/@xpath,$expr)">
1
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="not(contains($check,'1')) and
(substring-after(substring-after(@xpath,$xpath-expr),'/')
!= '')">
<xsl:call-template
name="constructXmlFragment">
<xsl:with-param name="path"
select="substring-after(substring-after(@xpath,$xpath-expr),'/')"
/>
<xsl:with-param name="value"
select="@xvalue" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="*" />
</xsl:element>
</xsl:template>
<!-- a template to construct an XPath expression,
for a given node -->
<xsl:template name="constructXPathExpr">
<xsl:param name="node" />
<xsl:param name="xpath" />
<xsl:choose>
<xsl:when test="$node/parent::*">
<xsl:call-template name="constructXPathExpr">
<xsl:with-param name="node"
select="$node/parent::*" />
<xsl:with-param name="xpath"
select="concat(name($node/parent::*),'/',$xpath)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/',$xpath)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- a template to generate a XML fragment -->
<xsl:template name="constructXmlFragment">
<xsl:param name="path" />
<xsl:param name="value" />
<xsl:choose>
<xsl:when test="contains($path,'/')">
<xsl:element
name="{substring-before($path,'/')}">
<xsl:call-template
name="constructXmlFragment">
<xsl:with-param name="path"
select="substring-after($path,'/')" />
<xsl:with-param name="value"
select="$value" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$path}">
<xsl:value-of select="$value" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
source.xml is -
<?xml version="1.0" encoding="UTF-8"?>
<employee>
<name>
<first>Bob</first>
</name>
</employee>
Given updates.xml as -
<?xml version="1.0" encoding="UTF-8"?>
<updates>
<elem xpath="/employee/address/country" xvalue="USA"
/>
<elem xpath="/employee/name/first" xvalue="Bill" />
</updates>
When the transformation is run as -
java net.sf.saxon.Transform source.xml main.xsl
The output received is -
<?xml version="1.0" encoding="UTF-8"?>
<employee>
<address>
<country>USA</country>
</address>
<name>
<first>Bill</first>
</name>
</employee>
I tested with some more test cases, by changing the
updates.xml as..
a)
<?xml version="1.0" encoding="UTF-8"?>
<updates>
<elem xpath="/employee/address/country" xvalue="USA"
/>
<elem xpath="/employee/name/first" xvalue="Bill" />
<elem xpath="/employee/name/last" xvalue="Gates" />
</updates>
The output received is -
<?xml version="1.0" encoding="UTF-8"?>
<employee>
<address>
<country>USA</country>
</address>
<name>
<last>Gates</last>
<first>Bill</first>
</name>
</employee>
b)
<?xml version="1.0" encoding="UTF-8"?>
<updates>
<elem xpath="/employee/id" xvalue="1" />
<elem xpath="/employee/address/country" xvalue="USA"
/>
<elem xpath="/employee/name/first" xvalue="Bill" />
<elem xpath="/employee/name/last" xvalue="Gates" />
</updates>
The output received is -
<?xml version="1.0" encoding="UTF-8"?>
<employee>
<id>1</id>
<address>
<country>USA</country>
</address>
<name>
<last>Gates</last>
<first>Bill</first>
</name>
</employee>
c)
<?xml version="1.0" encoding="UTF-8"?>
<updates>
<elem xpath="/employee/organization"
xvalue="Microsoft" />
<elem xpath="/employee/address/country" xvalue="USA"
/>
<elem xpath="/employee/name/first" xvalue="Bill" />
<elem xpath="/employee/name/last" xvalue="Gates" />
</updates>
The output received is -
<?xml version="1.0" encoding="UTF-8"?>
<employee>
<organization>Microsoft</organization>
<address>
<country>USA</country>
</address>
<name>
<last>Gates</last>
<first>Bill</first>
</name>
</employee>
Regards,
Mukul
--- Jianyu Lai <jlai@xxxxxxxx> wrote:
> Hi all,
>
> I'm rather new to XSL. I am struggling trying to
> come up with an xslt to
> solve the following problem:
>
> First I have the source xml that looks like this:
> <employee>
> <name>
> <first>Bob</first>
> </name>
> </employee>
>
> I have another xml (updates.xml) that contains
> information about how to
> update the above source. Notice that this
> updates.xml is dynamically
> generated, and its contents vary.
>
> <updates>
> <elem xpath="/employee/address/country"
> xvalue="USA" />
> <elem xpath="/employee/name/first" xvalue="Bill"
> />
> </updates>
>
> I want to write an xslt that reads information from
> updates.xml, and updates
> source.xml based on these criteria:
> - if xpath in updates.xml exist in source.xml,
> replace source xml node with
> xvalue;
> - otherwise, create xml node in source(recursively
> if necessary), with
> xvalue defined in updates.xml;
>
> Basically here is the result xml that I need:
> <employee>
> <name>
> <first>Bill</first>
> </name>
> <address>
> <country>USA</country>
> </address>
> </employee>
>
> Is this something that can be done by xslt? If so,
> could any one shed some
> light on this? Your help is greatly appreciated.
>
> Jianyu Lai
__________________________________
Do you Yahoo!?
Yahoo! Mail - Helps protect you from nasty viruses.
http://promotions.yahoo.com/new_mail
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|

Cart








