Search code examples
xmlxslt

Update or insert same node from the other XML using xslt


I have a source xml that contains a list of element which may or may not exists in target xml and target xml has some other xml element. The requirement is to update the second xml with the rule :loop at the element in the first xml. if node exists in the target xml then update the value, otherwise add the node to the target xml. I could hard code on field name f1 and f2 and f3 for my example below. But I am wondering if it is possible to make it generic so to have a piece of transformation code really saying "loop at the element in the first xml. if node exists in the target xml then update the value, otherwise add a new node" without coding on each node name?

Parameter XML

<root>
<f1>1<f1>
<f2>2<f2>
<f3>3<f3>
</root>

input XML

<root>
<f1>a<f1>
<FY>Y<fY>
<fz>Z<fz>
</root>

output XML

<root>
<f1>1<f1>
<f2>2<f2>
<f3>3<f3>
<FY>Y<fY>
<fz>Z<fz>
</root>

I am using XSLT Version 1.0

<xsl:param name="SourceData" />
    <xsl:variable name="Source_xml" select="parse-xml($SourceData)" />

and then

 <xsl:template match="f1">
  <f1><xsl:value-of select="$Source_xml//f1"/></f1>
 </xsl:template>
 <xsl:template match="f2">
  <f2><xsl:value-of select="$Source_xml//f2"/></f2>
 </xsl:template>
 <xsl:template match="f3">
  <f3><xsl:value-of select="$Source_xml//f3"/></f3>
 </xsl:template>

Solution

  • Given that (if parse-xml() works) you are using XSLT 3.0, you can use the xsl:merge instruction:

    <root>
      <xsl:merge>
        <xsl:merge-source 
               for-each-source="'doc1.xml', 'doc2.xml'"
               select="/*/*">
          <xsl:merge-key select="name()" order="ascending"/>
        </xsl:merge-source>
        <xsl:merge-action>
          <xsl:copy-of select="current-merge-group()[1]"/>
        </xsl:merge-action>
      </xsl:merge>
    </root>
    

    This assumes that both inputs are sorted by name. If not, there is an option sort-before-merge to force this.