2

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>
Parfait
  • 104,375
  • 17
  • 94
  • 125
Matt755921
  • 23
  • 3

1 Answers1

0

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>
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • Hi Parfait, the processor is embedded within a JAVA based vendor system, and is currently set up to execute the script in a single step. I will need to reconfigure the process for two step processing and will try your solution when back at work. Thank you for your prompt response to this conundrum of mine, it really has been causing me a headache, so to hear that others are also unaware of a single script method solution makes me significantly happier, thanks again – Matt755921 Jun 27 '20 at 18:25
  • Haha...after digging deeper, I was able to run a single XSLT script. See edit. However, order is different due to logic calls. Please advise if you need to retain order of *repeatedElementName*. In fact, in actual XML can this element be sorted alphabetically? Note: in the XML standard [order of elements does not matter](https://stackoverflow.com/q/4328867/1422451) but your vendor application may require it. – Parfait Jun 27 '20 at 20:00
  • Hi, Problem solved, many thanks this has helped me greatly, the vendor app does not require ordering in the repeated element stack. Thanks once again. – Matt755921 Jun 29 '20 at 17:41