1

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?

user7786267
  • 137
  • 12
  • What is the expected output for the two samples? – Martin Honnen Feb 05 '20 at 12:42
  • sorry xml example is wrong. Have now updated post with the correct xml example and expected xml example – user7786267 Feb 05 '20 at 13:50
  • What defines the order of `variable` elements in the result? Also please tell us which XSLT version or XSLT processor you use. – Martin Honnen Feb 05 '20 at 13:59
  • Trying . I don't really care what order the variable is in. I just want to end up with an xml that contains variables in both file and file2 variable to replace with file1 variable that has the same name. – user7786267 Feb 05 '20 at 14:10

2 Answers2

2

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>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
1

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.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • I'm using powershell to run the xslt transformation. I don't think it handles version 3. I really need the code in 2.0 or 1.0 – user7786267 Feb 05 '20 at 16:00
  • You can use any .NET code from Powershell, for instance the .NET version of Saxon 9. If you want to use the Microsoft XSLT processor then it only supports XSLT 1, neither 2 nor 3. I don't think there is anything in my posted code that is specific to XSLT 3, it should work with an XSLT 2 processor as well. XSLT 1 processors don't support `for-each-group`, I am sure someone else can help you with that if you need it. – Martin Honnen Feb 05 '20 at 16:07