to replace or not to replace - recursive call-template or apply-template in xsl 1.0 result Tree Frag annoyance

Viewed 43

I have a source document that needs to be merged and enriched with a RTF pulled / imported / calculated from another source. I need to merge the inbound content values from the fragment to replace existing where the equivalent node exists and add when they do not.

I have tried numerous attempts at solving this, but I am not an expert and am sure that I am over complicating the problem. Please see my feeble attempt and failure below with an example of source doc and imported fragment and the expected result. Please help me!

I have tried combining numerous similar problem solutions over the last few weeks, but the examples all use values rather than an imported fragment. I don't quite understand what I am doing wrong!

Source Doc XML:

<?xml version="1.0" encoding="UTF-8"?>
<sourceDocument>
   <lotsOfChildNodes />
   <thisBitToUpdate>
      <repeatedElements>
         <repeatedElement>
            <repeatedElementName>element1</repeatedElementName>
            <repeatedElementContent>Content1</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element2</repeatedElementName>
            <repeatedElementContent>Content2</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element3</repeatedElementName>
            <repeatedElementContent>Content3</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element4</repeatedElementName>
            <repeatedElementContent>Content4</repeatedElementContent>
         </repeatedElement>
      </repeatedElements>
   </thisBitToUpdate>
   <lotsMoreChildNodes />
</sourceDocument>

Imported RTF XML:

<repeatedElements>
   <repeatedElement>
      <repeatedElementName>element2</repeatedElementName>
      <repeatedElementContent>Content2Updated</repeatedElementContent>
   </repeatedElement>
   <repeatedElement>
      <repeatedElementName>element4</repeatedElementName>
      <repeatedElementContent>Content4Updated</repeatedElementContent>
   </repeatedElement>
   <repeatedElement>
      <repeatedElementName>element5</repeatedElementName>
      <repeatedElementContent>Content5New</repeatedElementContent>
   </repeatedElement>
   <repeatedElement>
      <repeatedElementName>element6</repeatedElementName>
      <repeatedElementContent>Content6New</repeatedElementContent>
   </repeatedElement>
</repeatedElements>

XSL:

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

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

<xsl:template match="/sourceDocument/lotsOfChildNodes/thisBitToUpdate/repeatedElements">
<xsl:variable name="nodeName" select="localName()" />
<xsl:variable name="newElementCount" select="1" />
<xsl:variable name="newElements" select="exsl:node-set($importedRepeatedElements)/repeatedElements" />
<element name="$nodeName">
    <call-template name="replaceDupesAndAdd">
<xsl:with-param name="positionCount" select="$newElementCount" />
<xsl:with-param name="elementsToMerge" select="$newElements" />
<xsl:with-param name="currentElements" select="." />
</call-template>
</element>

  
</xsl:template>

<xsl:template name="replaceDupesAndAdd">
<xsl:param name="positionCount" />
<xsl:param name="elementsToMerge" />
<xsl:param name="currentElements" />

<xsl:variable name="countElementsToMerge" select="count($elementsToMerge/repeatedElement)" />
<xsl:variable name="mergeElementName" >
<xsl:value-of select="$elementsToMerge/repeatedElement[position()=$positionCount]/repeatedElementName" />
</xsl:variable>

<xsl:if test="$positionCount &lt;= $countElementsToMerge">
<xsl:for-each select="exile:node-set($currentElements)/repeatedElement" >
<xsl:variable name="elementName">
<xsl:value-of select="./repeatedElementName">
</xsl:variable>
<xsl:choose>
<xsl:when test="$elementName=$mergeElementName">
<xsl:copy-of select="$elementsToMerge/repeatedElement[repeatedElementName=$mergeElementName]" />
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
<xsl:otherwise>
</xsl:choose>
<xsl:for-each>
<xsl:variable name="newCount">
<xsl:value-of select="number($positionCount+'1')" />
</xsl:variable>
<!-- call-template name="replaceDupesAndAdd">
<xsl:with-param name="positionCount" select="$newCount" />
<xsl:with-param name="currentElements" select="." />
<xsl:call-template -->
</xsl:if>


</xsl:template>

</xsl:stylesheet>

Desired Result Source Doc XML:

<?xml version="1.0"?>
<sourceDocument>
   <lotsOfChildNodes />
   <thisBitToUpdate>
      <repeatedElements>
         <repeatedElement>
            <repeatedElementName>element1</repeatedElementName>
            <repeatedElementContent>Content1</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element2</repeatedElementName>
            <repeatedElementContent>Content2Updated</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element3</repeatedElementName>
            <repeatedElementContent>Content3</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element4</repeatedElementName>
            <repeatedElementContent>Content4Updated</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element5</repeatedElementName>
            <repeatedElementContent>Content5New</repeatedElementContent>
         </repeatedElement>
         <repeatedElement>
            <repeatedElementName>element6</repeatedElementName>
            <repeatedElementContent>Content6New</repeatedElementContent>
         </repeatedElement>
      </repeatedElements>
   </thisBitToUpdate>
   <lotsMoreChildNodes />
</sourceDocument>
1 Answers

Consider pulling in fragment document nodes across different conditions: 1) only in source document, 2) also in source document, and 3) only in fragment document.

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
   
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="repeatedElements">
    <xsl:copy>
      <xsl:variable name="fragment" select="document('RepeatElementsMergeDoc_Fragment.xml')/descendant::repeatedElement"/>
      <xsl:variable name="curr_val" select="repeatedElement/repeatedElementName"/>
      
      <xsl:copy-of select="repeatedElement[not(repeatedElementName = $fragment/repeatedElementName)]"/>   
      <xsl:copy-of select="$fragment[repeatedElementName = $curr_val]" /> 
      <xsl:copy-of select="$fragment[not(repeatedElementName = $curr_val)]" />    
    </xsl:copy>
  </xsl:template>  
    
</xsl:stylesheet>

Output

<?xml version="1.0" encoding="UTF-8"?>
<sourceDocument>
  <lotsOfChildNodes/>
  <thisBitToUpdate>
    <repeatedElements>
      <repeatedElement>
        <repeatedElementName>element1</repeatedElementName>
        <repeatedElementContent>Content1</repeatedElementContent>
      </repeatedElement>
      <repeatedElement>
        <repeatedElementName>element3</repeatedElementName>
        <repeatedElementContent>Content3</repeatedElementContent>
      </repeatedElement>
      <repeatedElement>
        <repeatedElementName>element2</repeatedElementName>
        <repeatedElementContent>Content2Updated</repeatedElementContent>
      </repeatedElement>
      <repeatedElement>
        <repeatedElementName>element4</repeatedElementName>
        <repeatedElementContent>Content4Updated</repeatedElementContent>
      </repeatedElement>
      <repeatedElement>
        <repeatedElementName>element5</repeatedElementName>
        <repeatedElementContent>Content5New</repeatedElementContent>
      </repeatedElement>
      <repeatedElement>
        <repeatedElementName>element6</repeatedElementName>
        <repeatedElementContent>Content6New</repeatedElementContent>
      </repeatedElement>
    </repeatedElements>
  </thisBitToUpdate>
  <lotsMoreChildNodes/>
</sourceDocument>
Related