MartinKahn
MartinKahn

Reputation: 137

Merging two xml files using xslt after reading a third xml file

I have an xml file as below propNode.xml

<NODES>
  <NODE>
<NODELINE CLASS="Item" TYPE="Item" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName" />
    <ATTR_NODE NAME="myDesc />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item1" TYPE="Item1" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName1" />
    <ATTR_NODE NAME="myDesc1" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item2" TYPE="Item2" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName2" />
    <ATTR_NODE NAME="myDesc2" />
  </ATTR_NODES>
</NODELINE>
</NODE>
</NODES>

I want to read this xml and using it, I need to merge the following two xml files source.xml

<NODES>
  <NODE>
<NODELINE CLASS="Item" TYPE="Item" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName" VALUE="myNameValue" />
    <ATTR_NODE NAME="myDesc" VALUE="test-myDescValue" />
<ATTR_NODE NAME="myId" VALUE="test-myIdValue" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item1" TYPE="Item1" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName1" VALUE="myNameValue1" />
    <ATTR_NODE NAME="myDesc1" VALUE="myDescValue1"/>
<ATTR_NODE NAME="myId1" VALUE="myIdValue1" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item2" TYPE="Item2" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName2" VALUE="test-myNameValue2" />
    <ATTR_NODE NAME="myDesc2" VALUE="myDescValue2"/>
<ATTR_NODE NAME="myId2" VALUE="test-myIdValue2" />
  </ATTR_NODES>
</NODELINE>
  </NODE>
</NODES>

And target.xml

<NODES>
  <NODE>
<NODELINE CLASS="Item" TYPE="Item" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName" VALUE="myNameValue" />
    <ATTR_NODE NAME="myDesc" VALUE="myDescValue" />
<ATTR_NODE NAME="myId" VALUE="myIdValue" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item1" TYPE="Item1" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName1" VALUE="myNameValue1" />
    <ATTR_NODE NAME="myDesc1" VALUE="myDescValue1"/>
<ATTR_NODE NAME="myId1" VALUE="myIdValue1" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item2" TYPE="Item2" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName2" VALUE="myNameValue2" />
    <ATTR_NODE NAME="myDesc2" VALUE="myDescValue2"/>
<ATTR_NODE NAME="myId2" VALUE="myIdValue2" />
  </ATTR_NODES>
</NODELINE>
  </NODE>
</NODES>

The condition is reading the propNode.xml, if the value of @NAME matches in the source.xml and target.xml, then the values of @VALUE in the source.xml and target.xml needs to be compared and an output xml should be created as below:

desiredOutput.xml

<NODES>
  <NODE>
<NODELINE CLASS="Item" TYPE="Item" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName" SRCVALUE="myNameValue" TGTVALUE="myNameValue" ISDIFF="false" />
    <ATTR_NODE NAME="myDesc" SRCVALUE="test-myDescValue" TGTVALUE="myDescValue" ISDIFF="true" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item1" TYPE="Item1" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName1" SRCVALUE="myNameValue1" TGTVALUE="myNameValue1" ISDIFF="false" />
    <ATTR_NODE NAME="myDesc1" SRCVALUE="myDescValue1" TGTVALUE="myDescValue1" ISDIFF="false" />
  </ATTR_NODES>
</NODELINE>
<NODELINE CLASS="Item2" TYPE="Item2" >
  <ATTR_NODES>
    <ATTR_NODE NAME="myName2" SRCVALUE="test-myNameValue2" TGTVALUE="myNameValue2" ISDIFF="true" /> />
    <ATTR_NODE NAME="myDesc2" SRCVALUE="myDescValue2" TGTVALUE="myDescValue2" ISDIFF="false" />
  </ATTR_NODES>
</NODELINE>
  </NODE>
</NODES>

The desiredOutput.xml should contain both the values of @VALUE from the source and the target if the value of @NAME selected in the propNode.xml. If the values of @VALUE are different then @ISDIFF should have a value "true" or else "false".

Is this whole operation possible using xslt? Something like generating xsl using the propNode.xml and then applying it on the source.xml and the target.xml to generate the desired output? How would this xsl look like?

Upvotes: 2

Views: 159

Answers (1)

StuartLC
StuartLC

Reputation: 107357

Assuming that only the nodes (ATTR_NODE) present in propNode.xml need to be tested, the following XSLT should do the job:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:my="some.uri" version="1.0">

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

    <xsl:template match="ATTR_NODE">
        <xsl:variable name="NodeLine" select="../../@CLASS" />
        <xsl:variable name="AttrName" select="@NAME" />
        <xsl:variable name="SrcValue" select="document('source.xml')//NODELINE[@CLASS=$NodeLine]//ATTR_NODE[@NAME=$AttrName]/@VALUE" />
        <xsl:variable name="TgtValue" select="document('target.xml')//NODELINE[@CLASS=$NodeLine]//ATTR_NODE[@NAME=$AttrName]/@VALUE" />
        <xsl:element name="ATTR_NODE">
            <xsl:attribute name="NAME">
                <xsl:value-of select="$AttrName"/>
            </xsl:attribute>
            <xsl:attribute name="SRCVALUE">
                <xsl:value-of select="$SrcValue" />
            </xsl:attribute>
            <xsl:attribute name="TGTVALUE">
                <xsl:value-of select="$TgtValue" />
            </xsl:attribute>
            <xsl:attribute name="ISDIFF">
                <xsl:value-of select="$SrcValue!=$TgtValue" />
            </xsl:attribute>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

The identity template is used to traverse and copy propNode, with special processing for each ATTR_NODE checks VALUE in source and target, and then evaluated for equality. I've assumed that NODE_LINE/@CLASS is sufficient to establish the identity of the NODE_LINE - if it isn't, then you'll need to add additional checking e.g. @TYPE as well.

The xslt is run against propnode.xml, and source.xml and target.xml must be present in the same folder.

Output

<?xml version="1.0" encoding="utf-8"?>
<NODES>
    <NODE>
        <NODELINE CLASS="Item" TYPE="Item">
            <ATTR_NODES>
                <ATTR_NODE NAME="myName" SRCVALUE="myNameValue" TGTVALUE="myNameValue" ISDIFF="false" />
                <ATTR_NODE NAME="myDesc" SRCVALUE="test-myDescValue" TGTVALUE="myDescValue" ISDIFF="true" />"
            </ATTR_NODES>
        </NODELINE>
        <NODELINE CLASS="Item1" TYPE="Item1">
            <ATTR_NODES>
                <ATTR_NODE NAME="myName1" SRCVALUE="myNameValue1" TGTVALUE="myNameValue1" ISDIFF="false" />
                <ATTR_NODE NAME="myDesc1" SRCVALUE="myDescValue1" TGTVALUE="myDescValue1" ISDIFF="false" />
            </ATTR_NODES>
        </NODELINE>
        <NODELINE CLASS="Item2" TYPE="Item2">
            <ATTR_NODES>
                <ATTR_NODE NAME="myName2" SRCVALUE="test-myNameValue2" TGTVALUE="myNameValue2" ISDIFF="true" />
                <ATTR_NODE NAME="myDesc2" SRCVALUE="myDescValue2" TGTVALUE="myDescValue2" ISDIFF="false" />
            </ATTR_NODES>
        </NODELINE>
    </NODE>
</NODES>

Upvotes: 1

Related Questions