I want to merge 2 XMLs based on a particular condition.

e.g: Sample1.xml:

<rootNode>
<header>
<title agg="sum">1</title>
<records agg="sum">10</records>
<number agg="min">5</number>
<header>
</rootNode>

Sample2.xml

<rootNode>
<header>
<title agg="sum">2</title>
<records agg="sum">20</records>
<number agg="min">15</number>
<header>
</rootNode>

I want that these two xml files should be merged based on the attribute specified within the tag i.e "agg".if agg="sum" then title from sample1.xml and sample2.xml should get added. Output should be like:

output.xml

<rootNode>
<header>
<title agg="sum">3</title>
<records agg="sum">30</records>
<number agg="min">5</number>
<header>
</rootNode>

Kindly provide XSLT for this. Thanks.

You simply pass the stylesheet the second file location as the parameter InputFileTwo and it will work when run on the primary file, it doesnt matter which way round you do it, however the XSLT will look at the attributes of the primary file to determine concatenation (nothing said otherwise in the requirements).

XSLT Stylesheet

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

  <xsl:output indent ="yes" method="xml"/>
  <xsl:strip-space elements ="*"/>

  <xsl:param name="InputFileTwo">C:\Users\maskew\Documents\DaniWeb\TestXMLTwo.xml</xsl:param>

  <xsl:template match="/">
    <xsl:call-template name="ConcatFiles"/>
  </xsl:template>

  <xsl:template name="ConcatFiles">
    <xsl:variable name="tempStoreDocTwo" select ="document($InputFileTwo)/rootNode/header" />

    <xsl:element name="rootNode">
      <xsl:element name="header">

        <xsl:for-each select="rootNode/header/*">
          <xsl:variable name="pos" select="position()" />
          <xsl:choose>

            <xsl:when test="./@agg = 'sum'">
              <xsl:variable name="tempElementDocTwo" select ="$tempStoreDocTwo/*[$pos]"/>
              <xsl:element name="{name(.)}">
                <xsl:value-of select=". + $tempElementDocTwo"/>
              </xsl:element>
            </xsl:when>

            <xsl:otherwise>
              <xsl:element name="{name(.)}">
                <xsl:value-of select="."/>
              </xsl:element>
            </xsl:otherwise>

          </xsl:choose>
        </xsl:for-each>

      </xsl:element>
    </xsl:element>

  </xsl:template>

</xsl:stylesheet>

Edited 4 Years Ago by Mike Askew

Noticed I completely ignored replicating attributes, new XSLT:

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

  <xsl:output indent ="yes" method="xml"/>
  <xsl:strip-space elements ="*"/>

  <xsl:param name="InputFileTwo">C:\Users\maskew\Documents\DaniWeb\TestXMLTwo.xml</xsl:param>

  <xsl:template match="/">
    <xsl:call-template name="ConcatFiles"/>
  </xsl:template>

  <xsl:template name="ConcatFiles">
    <xsl:variable name="tempStoreDocTwo" select ="document($InputFileTwo)/rootNode/header" />

    <xsl:element name="rootNode">
      <xsl:element name="header">

        <xsl:for-each select="rootNode/header/*">
          <xsl:variable name="pos" select="position()" />
          <xsl:choose>

            <xsl:when test="./@agg = 'sum'">
              <xsl:variable name="tempElementDocTwo" select ="$tempStoreDocTwo/*[$pos]"/>
              <xsl:element name="{name(.)}">
                <xsl:attribute name ="agg">sum</xsl:attribute>
                <xsl:value-of select=". + $tempElementDocTwo"/>
              </xsl:element>

            </xsl:when>

            <xsl:otherwise>
              <xsl:element name="{name(.)}">
                <xsl:attribute name="agg">
                  <xsl:value-of select="./@agg"/>
                </xsl:attribute>
                <xsl:value-of select="."/>
              </xsl:element>

            </xsl:otherwise>

          </xsl:choose>
        </xsl:for-each>

      </xsl:element>
    </xsl:element>

  </xsl:template>

</xsl:stylesheet>

Worth noting also a simplified version of this, written by Dimitre Novatchev of StackOverflow.

I did edit in the attribute writing and correct a element name mapping issue.

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

  <xsl:param name="pDoc2" />

  <xsl:variable name="vDoc2" select="document('')/*/xsl:param"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="header/*[@agg='sum']">
    <xsl:element name="{name(.)}">
      <xsl:attribute name ="agg">sum</xsl:attribute>
      <xsl:value-of select=
    ". + $vDoc2/*/header/*[name()=name(current()) and @agg='sum']"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Edited 4 Years Ago by Mike Askew

This is the generic xslt. Please suggest if someone has better approach

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="xml" indent="yes" ></xsl:output>
    <xsl:strip-space elements="*" ></xsl:strip>
    <xsl:variable select="document('Second.xml')" name="file2" ></xsl:variable>
    <xsl:template match="/">
        <xsl:call-template name="main" ></xsl:call>
    </xsl:template>
    <xsl:template match="@*">
        <xsl:copy>
            <xsl:apply-templates select="." ></xsl:apply>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="node()" name="main">
        <xsl:copy>
            <xsl:apply-templates select="@*" ></xsl:apply>
            <xsl:variable name="nod" select="name()" ></xsl:variable>
            <xsl:variable name="var" select="text()" ></xsl:variable>
            <xsl:variable name="xpath">
                <xsl:call-template name="name">
                    <xsl:with-param name="cNode" select="$nod" ></xsl:with>
                </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="second">
                <xsl:call-template name="getNodeValue">
                    <xsl:with-param name="pExpression" select="$xpath" ></xsl:with>
                </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="test">
                <xsl:for-each select="@*">
                    <xsl:choose>
                        <xsl:when test="name()='Agg'">
                            <xsl:variable name="flag" select="string('true')" ></xsl:variable>
                            <xsl:if test=".='sum'">
                                <xsl:variable name="eval" select="number($second)+number($var)" ></xsl:variable>
                                <xsl:choose>
                                    <xsl:when test="string($eval)='NaN'">
                                        <xsl:value-of select="concat($second,$var)" ></xsl:value>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:value-of select="$eval" ></xsl:value>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:if>
                            <xsl:if test=".='min'">
                                <xsl:variable name="eval" select="number($second)-number($var)" ></xsl:variable>
                                <xsl:choose>
                                    <xsl:when test="string($eval)='NaN'">
                                        <xsl:call-template name="compareString">
                                            <xsl:with-param name="str1" select="$var" ></xsl:with>
                                            <xsl:with-param name="str2" select="$second" ></xsl:with>
                                        </xsl:call-template>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:choose>
                                            <xsl:when test="$eval > 0">
                                                <xsl:value-of select="$var" ></xsl:value>
                                            </xsl:when>
                                            <xsl:otherwise>
                                                <xsl:value-of select="$second" ></xsl:value>
                                            </xsl:otherwise>
                                        </xsl:choose>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:if>
                            <xsl:if test=".='max'">
                                <xsl:variable name="eval" select="number($var)-number($second)" ></xsl:variable>
                                <xsl:choose>
                                    <xsl:when test="string($eval)='NaN'">
                                        <xsl:call-template name="maxString">
                                            <xsl:with-param name="str1" select="$var" ></xsl:with>
                                            <xsl:with-param name="str2" select="$second" ></xsl:with>
                                        </xsl:call-template>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:choose>
                                            <xsl:when test="$eval > 0">
                                                <xsl:value-of select="$var" ></xsl:value>
                                            </xsl:when>
                                            <xsl:otherwise>
                                                <xsl:value-of select="$second" ></xsl:value>
                                            </xsl:otherwise>
                                        </xsl:choose>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:if>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="string('false')" ></xsl:value>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each>
            </xsl:variable>
            <xsl:choose>
                <xsl:when test="$test='false'">
                    <xsl:value-of select="$var" ></xsl:value>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="replace($test,string('false'),'')" ></xsl:value>
                </xsl:otherwise>
            </xsl:choose>

            <xsl:apply-templates select="child::*" ></xsl:apply>
        </xsl:copy>
    </xsl:template>

    <xsl:template name="name">
        <xsl:param name="cNode" select="name()" ></xsl:param>
        <xsl:for-each select="ancestor-or-self::*">
            <xsl:value-of select="name()" ></xsl:value>
            <xsl:if test="not(name()=$cNode)">
                <xsl:text>/</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="getNodeValue">
        <xsl:param name="pExpression" ></xsl:param>
        <xsl:param name="pCurrentNode" select="$file2" ></xsl:param>
        <xsl:choose>
            <xsl:when test="not(contains($pExpression, '/'))">
                <xsl:value-of select="$pCurrentNode/*[name()=$pExpression]" ></xsl:value>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="getNodeValue">
                    <xsl:with-param name="pExpression"
                        select="substring-after($pExpression, '/')" ></xsl:with>
                    <xsl:with-param name="pCurrentNode"
                        select="$pCurrentNode/*[name()=substring-before($pExpression, '/')]" ></xsl:with>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="maxString">
        <xsl:param name="str1" ></xsl:param>
        <xsl:param name="str2" ></xsl:param>
        <xsl:variable name="test" select="compare($str1,$str2)" ></xsl:variable>
        <xsl:choose>
            <xsl:when test="$test = -1">
                <xsl:value-of select="$str2" ></xsl:value>
            </xsl:when>
            <xsl:when test="$test = 0">
                <xsl:value-of select="$str1" ></xsl:value>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$str1" ></xsl:value>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="compareString">
        <xsl:param name="str1" ></xsl:param>
        <xsl:param name="str2" ></xsl:param>
        <xsl:variable name="test" select="compare($str1,$str2)" ></xsl:variable>
        <xsl:choose>
            <xsl:when test="$test = -1">
                <xsl:value-of select="$str1" ></xsl:value>
            </xsl:when>
            <xsl:when test="$test = 0">
                <xsl:value-of select="$str1" ></xsl:value>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$str2" ></xsl:value>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
This article has been dead for over six months. Start a new discussion instead.