Re: [xsl] Merging two xml documents using xslt

Subject: Re: [xsl] Merging two xml documents using xslt
From: Mukul Gandhi <mukul_gandhi@xxxxxxxxx>
Date: Fri, 4 Feb 2005 08:11:37 -0800 (PST)
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

Current Thread