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[(_at_)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(_at_)esri(_dot_)com> 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
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--