Search code examples
javaxmlxsltsaxondomparser

how to copy data from one xml files in to another using java xslt


  1. I need to replace all ? in file1.xml with element value present in File2.xml.
  2. And also need to remove extra element node present in File1.xml whose value is not present in File2.xml. (i.e. In my sample input need to remove totalDueWithoutTax element from file1.xml as it is not present in File2.xml.
  3. There may be the cases where we have multiple elements in File2 but only one corresponsing element in File1. Like in my below example we have two salesInfo element in File2 but in File1 we have only one. In that case as a expected output we need both salesInfo element of File2.
  4. This is just sample input. In real scenario input xmls can be different means we can get different format of xmls everytime.

Please suggest me how to do that in java. It would be better if we can achieve this using xslt.

        **File 1 -- input xml**
       <order>
            <orderId>?</orderId>
             <sales>
              <salesInfo>
                <salesChannel>?</salesChannel>
                <senderSystemId>?</senderSystemId>
                <applicationId>?</applicationId>
                <totalDueWithoutTax>?</totalDueWithoutTax>
              </salesInfo>
              <salesid>?</salesid>
           </sales>
        </order>
        
        **File 2 -- input xml**
    
        <order>
          <orderId>4567</orderId>
          <sales>
              <salesInfo>
                <salesChannel>abc</salesChannel>
                <senderSystemId>def</senderSystemId>
                <applicationId>123</applicationId>
                <esignatureCaptureMode>INLINE</esignatureCaptureMode>
              </salesInfo>
              <salesInfo>
                <salesChannel>xyz</salesChannel>
                <senderSystemId>uvw</senderSystemId>
                <applicationId>234</applicationId>
                <esignatureCaptureMode>outline</esignatureCaptureMode>
              </salesInfo>
              <salesid>789</salesid>
        </sales>
        </order>
        
        **Expected output:**
        
        <order>
            <orderId>4567</orderId>
            <sales>
              <salesInfo>
                <salesChannel>abc</salesChannel>
                <senderSystemId>def</senderSystemId>
                <applicationId>123</applicationId>
              </salesInfo>
              <salesInfo>
               <salesChannel>xyz</salesChannel>
                <senderSystemId>uvw</senderSystemId>
                <applicationId>234</applicationId>
              </salesInfo>
              <salesid>789</salesid>
          </sales>
        </order>

Solution

  • You could consider a group/merge problem:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="3.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="#all"
      xmlns:mf="http://example.com/mf"
      expand-text="yes">
      
      <xsl:param name="doc2" select="doc('file2.xml')"/>>
    
      <xsl:output method="xml" indent="yes"/>
      
      <xsl:function name="mf:merge" as="node()*">
        <xsl:param name="nodes1" as="node()*"/>
        <xsl:param name="nodes2" as="node()*"/>
        <xsl:for-each-group select="$nodes1, $nodes2" group-by="node-name()">
          <xsl:choose>
            <xsl:when test="not(*) and . = '?' and current-group()[2]">
              <xsl:apply-templates select="current-group()[2]"/>
            </xsl:when>
            <xsl:when test="current-group()[1][*] and current-group()[2][*]">
              <xsl:copy>
                <xsl:sequence select="mf:merge(*, current-group()[2]/*)"/>
              </xsl:copy>
            </xsl:when>
          </xsl:choose>
        </xsl:for-each-group>
      </xsl:function>
      
      <xsl:template match="/*">
        <xsl:copy>
          <xsl:sequence select="mf:merge(*, $doc2/*/*)"/>
        </xsl:copy>
      </xsl:template>
        
      <xsl:mode on-no-match="shallow-copy"/>
    
    </xsl:stylesheet>
    

    Depending on the structure or the merging requirements using xsl:for-each-group select="$nodes1, $nodes2" group-by="path(.)" instead of <xsl:for-each-group select="$nodes1, $nodes2" group-by="node-name()"> might be more suitable, for instance if an element can have several child elements of the same name.