Nps
Nps

Reputation: 1658

Copy data from one XML doc to another using XSLT

I have to copy data of node element from file1.xml to file2.xml. file1.xml

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <header>
    <AsofDate>31-Dec-2012</AsofDate>
    <FundName>This is Sample Fund</FundName>
    <Description>This is test description</Description>
  </header>
</root>

file2.xml

<?xml version="1.0" encoding="utf-8" ?>
<root id="1">
  <header id="2">
    <AsofDate id="3"/>
    <FundName id="4" />
    <Description id="5" />
  </header>
</root>

after merging file1.xml into file2.xml, result should look below:

<?xml version="1.0" encoding="utf-8" ?>
<root id="1">
  <header id="2">
    <AsofDate id="3">31-Dec-2012</AsofDate>
    <FundName id="4">This is Sample Fund</FundName>
    <Description id="5">This is test description</Description>
  </header>
</root>

I am using below XSLT to transform file.

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Below is the code is used to perform transformation:

    XslCompiledTransform tf = new XslCompiledTransform();
    tf.Load("TranFile.xsl");

    tf.Transform("file1.xml", "file2.xml");

but above code is overwriting the file2 content with file1.xml content. This is just sample XML. In real case we don't know name of nodes and hierarchy of the xml file. But whatever structure would be will be same for both file and scenario will be exactly same. I am new to XSLT and not sure is this right approach to accomplish the result. Is it really possible to achieve result through XSLT.

Upvotes: 2

Views: 4739

Answers (1)

Pablo Pozo
Pablo Pozo

Reputation: 1920

The solution that I post is written having in mind the following:

  • The only thing that need to be merged are the attributes. Text and element nodes are copied as they appear from file1.xml.

  • The @id attributes are not numbered sequentially in file2.xml so the @id's in file2.xml could be (for example) 121 432 233 12 944 instead of 1 2 3 4 5. If the case is the latter then you would not need file2.xml to generate the desired output.

  • The document() function can be used to access files different than the current one. If XslCompiledTransform is giving an error when using the document function I would suggest to follow this using document() function in .NET XSLT generates error . I am using a different XSLT processor (xsltproc) and it works fine.

This solution is based on keeping a reference to the external file, so each time that we process an element in file1.xml the reference is moved to point at the same element in file2.xml. This can be done because according to the problem, both files present the same element hierarchy.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="no"/>

    <!-- Match the document node as an entry point for matching the files -->
    <xsl:template match="/">
        <xsl:apply-templates select="node()">
            <xsl:with-param name="doc-context" select="document('file2.xml')/node()" />
        </xsl:apply-templates>
    </xsl:template>

    <!-- In this template we copy the elements and text nodes from file1.xml and
         we merge the attributes from file2.xml with the attributes in file1.xml -->
    <xsl:template match="node()">
        <!-- We use this parameter to keep track of where we are in file2.xml by
             mimicking the operations that we do in the current file. So we are at
             the same position in both files at the same time. -->
        <xsl:param name="doc-context" />

        <!-- Obtain current position in file1.xml so we know where to look in file2.xml -->
        <xsl:variable name="position" select="position()" />

        <!-- Copy the element node from the current file (file1.xml) -->
        <xsl:copy>
            <!-- Merge attributes from file1.xml with attributes from file2.xml -->
            <xsl:copy-of select="@*|$doc-context[position() = $position]/@*" />
            <!-- Copy text nodes and process children -->
            <xsl:apply-templates select="node()">
                <xsl:with-param name="doc-context" select="$doc-context/node()" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 4

Related Questions