xsl-list
[Top] [All Lists]

Re: Merging two xml documents using xslt

2005-02-04 09:11:37
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>
--~--