orange-lily
orange-lily

Reputation: 221

XSL merge amend and add

I've worked out how to merge two XML file and amend the matching attribute.

I'm now struggling to work out how to add file2 node (based on attribute name) if it does not exist in file1

!--File1 xml -->
<stylesheet>
<variable name="Test1" />
<variable name="Test2" select="'yy'"/>
<variable name="Test3" select="'xx'"/>
</sytlesheet>

<!--File2 xml -->
<stylesheet>
<variable name="Test" select="'x'" />
<variable name="Test2" select="'y'" />
<variable name="Test3" select="'z'" />
<variable name="Test4" select="'dd'" />
</sytlesheet>

<!--Expected xml result-->
<stylesheet>
<variable name="Test1" />
<variable name="Test" select="'x'" />
<variable name="Test2" select="'y'" />
<variable name="Test3" select="'z'" />
<variable name="Test4" select="'dd'" />
</sytlesheet>

Here's the xsl file I have:

<xsl:param name="fileName" select="'file2'" />

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

<xsl:template match="stylesheet/variable"> 
<xsl:copy>
    <xsl:apply-templates select="@*" />
    <xsl:if test="document($fileName)/stylesheet/variable[@name = current()/@name]">
        <xsl:attribute name="value">
            <xsl:value-of select="document($fileName)/stylesheet/variable[@name = current()/@name]/@select"/>
        </xsl:attribute>    
    </xsl:if>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

I found how to Merge two xml files with XSLT but unable to work out how to apply propose solution to my xsl. Anyone can help?

Upvotes: 1

Views: 194

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 117140

You could do simply:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<!-- path to file2.xml -->
<xsl:param name="file2">file2.xml</xsl:param>

<xsl:template match="/stylesheet">
    <xsl:copy>
        <!-- local items not overridden by external items -->
        <xsl:copy-of select="variable[not(@name=document($file2)/stylesheet/variable/@name)]"/>
        <!-- ALL external items (override AND add) -->
        <xsl:copy-of select="document($file2)/stylesheet/variable"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Upvotes: 2

Martin Honnen
Martin Honnen

Reputation: 167716

Seems like a task for grouping the variable elements from both documents by the name and then use the last() item in each group to make sure only one item is output:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:param name="file2">
<stylesheet>
<variable name="Test" select="'x'" />
<variable name="Test2" select="'y'" />
<variable name="Test3" select="'z'" />
<variable name="Test4" select="'dd'" />
</stylesheet>
  </xsl:param>

  <xsl:output indent="yes"/>

  <xsl:template match="stylesheet">
      <xsl:copy>
          <xsl:for-each-group select="variable, $file2/stylesheet/variable" group-by="@name">
              <xsl:copy-of select="current-group()[last()]"/>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bwe3c2 and the snippet above has the secondary input inlined for compactness and completeness of the example but you can of course use <xsl:param name="file2" select="doc('file2.xml')"/> instead in your real use case.

Upvotes: 1

Related Questions