1

Please i want to know how i can merge two xml files into one xml file using xslt. I have these both xml files that i want to merge into an xml file as shown in the expected output.I want to include the each node Hn from the second file to the corresponding block with same number in the file 1.

file1:

<Test>
  <R1>
    <Th1>
        here are some instruction.
    </Th1>
  </R1>

  <R2>
    <Th2>
        here are some instruction.
    </Th2>
  </R2>

  <R3>
    <Th3>
        here are some instruction.
    </Th3>
  </R3>
</Test>

file 2:

<test1>
  <H1>
    here are some instruction.
  </H1>

  <H2>
    here are some instruction.
  </H2>

  <H3>
    here are some instruction.
  </H3>
</test1>    

and here is the expected output:

<test2>
  <R1>
    <H1>
        here are some instruction.
    </H1>
    <Th1>
        here are some instruction.
    </Th1>
  </R1>

  <R2>
    <H2>
        here are some instruction.
    </H2>
    <Th2>
        here are some instruction.
    </Th2>
  </R2>

  <R3>
    <H3>
        here are some instruction.
    </H3>
    <Th3>
        here are some instruction.
    </Th3>
  </R3>

</test2>

Thanks for your help.

Franky N.
  • 79
  • 1
  • 9
  • Hello Franky, you have to make use of the document() function. Try setting up an XSLT and see how far you get. thank you, Peter – Peter Sep 14 '12 at 11:37
  • Hello peter, i'm not expert with xslt. I just started to work with xslt and i don't know more thing about it. But i will make some researches on google concerning that fucntion. Thanks already to give a trick. – Franky N. Sep 14 '12 at 11:53

1 Answers1

2

I. This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>

 <xsl:variable name="vDigits" select="'0123456789'"/>
 <xsl:variable name="vDoc2" select="document('file:///c:/temp/delete/file2.xml')"/> 

 <xsl:template match="/*">
     <test2><xsl:apply-templates/></test2>
 </xsl:template>

 <xsl:template match="/*/*">
  <xsl:copy>
   <xsl:text>&#xA;</xsl:text>
   <xsl:copy-of select=
    "$vDoc2/*/*
      [translate(name(),translate(name(),$vDigits,''),'')
      =
       translate(name(current()),translate(name(current()),$vDigits,''),'')
      ]"/>
    <xsl:copy-of select="node()"/>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

When applied on the first XML document (file1.xml):

<Test>
  <R1>
    <Th1>
        here are some instruction.
    </Th1>
  </R1>

  <R2>
    <Th2>
        here are some instruction.
    </Th2>
  </R2>

  <R3>
    <Th3>
        here are some instruction.
    </Th3>
  </R3>
</Test>

and having the second XML document reside at c:\temp\delete\file2.xml:

<test1>
  <H1>
    here are some instruction.
  </H1>

  <H2>
    here are some instruction.
  </H2>

  <H3>
    here are some instruction.
  </H3>
</test1>

produces the wanted, correct result:

<test2>
  <R1>
<H1>
    here are some instruction.
  </H1>
    <Th1>
        here are some instruction.
    </Th1>
  </R1>

  <R2>
<H2>
    here are some instruction.
  </H2>
    <Th2>
        here are some instruction.
    </Th2>
  </R2>

  <R3>
<H3>
    here are some instruction.
  </H3>
    <Th3>
        here are some instruction.
    </Th3>
  </R3>
</test2>

Explanation:

Proper use of the "double-translate" method first demoed by Michael Kay.


II. XSLT 2.0 solution:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>

 <xsl:variable name="vDigits" select="'0123456789'"/>
 <xsl:variable name="vDoc2" select="document('file:///c:/temp/delete/file2.xml')"/> 

 <xsl:template match="/*">
     <test2><xsl:apply-templates/></test2>
 </xsl:template>

 <xsl:template match="/*/*">
  <xsl:if test="not(matches(name(), '^.+\d+$'))">
    <xsl:message terminate="yes">
     Element Name doesn't end with number: <xsl:sequence select="name()"/>
    </xsl:message>
  </xsl:if>
  <xsl:copy>
   <xsl:text>&#xA;</xsl:text>
   <xsl:copy-of select=
    "$vDoc2/*/*
      [replace(name(),'^.+(\d+)$', '$1')
      =
       replace(name(current()),'^.+(\d+)$', '$1')
      ]"/>
    <xsl:copy-of select="node()"/>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

Explanation:

Proper use of the standard XPath 2.0 functions matches() and replace()

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • hello Dimitre, Thanks for this solution. It walks very well. But i want to know the purpose and the use of the variable name vDigits. Is there any possibility to get automatically the path for the file2.xml, so that i don't need always to store this file in c:\temp\delete\...? Thanks – Franky N. Sep 14 '12 at 12:33
  • @FrankyN., Yes, you can specify a global/external (child of `xsl:stylesheet`) `xsl:param` for this purpose. Such parameter can be set externally from the invoker of the XSLT transformation -- as this is implementation-dependent, you need to read the documentation of the specific XSLT processor you are using. – Dimitre Novatchev Sep 14 '12 at 12:45
  • @FrankyN., The role of `$vDigits` -- it serves to find all the digits used in the element name. First we find all non-digits (in the inner `translate()`), then we strip-off all non-digits from the name -- in the outer translate. What remains is just all the digits that the name contains. – Dimitre Novatchev Sep 14 '12 at 12:47