title: XML combine documents
body: <p>I am working on Boomi interface and I need to combine individual xml documents in to single output documents . The standard combine document step is not working properly.</p>
  
  <p>All xml documents are of same structure .</p>
  
  <p>First Document </p>
  
  <pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
  &lt;EMPLEADOS&gt;
    &lt;EMPLEADO TIPO="A" NUMERO="123"&gt;
      &lt;PROCESO PERIODO="201603" TT="MN" PAC="9999" /&gt;
      &lt;SECCION ID="ETACIV"&gt;
        &lt;CAMPO ID="ETA_ETCNOM" SEC=" " FECHA=" "&gt;abc&lt;/CAMPO&gt;
      &lt;/SECCION&gt;
    &lt;/EMPLEADO&gt;
  &lt;/EMPLEADOS&gt;
  </code></pre>
  
  <p>Second document</p>
  
  <pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
  &lt;EMPLEADOS&gt;
    &lt;EMPLEADO TIPO="A" NUMERO="123"&gt;
      &lt;PROCESO PERIODO="201603" TT="MN" PAC="9999" /&gt;
      &lt;SECCION ID="SADMIN "&gt;
        &lt;CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/03/2015"&gt;01/03/2015&lt;/CAMPO&gt;
      &lt;/SECCION&gt;
    &lt;/EMPLEADO&gt;
  &lt;/EMPLEADOS&gt;
  </code></pre>
  
  <p>Third document</p>
  
  <pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
  &lt;EMPLEADOS&gt;
    &lt;EMPLEADO TIPO="A" NUMERO="123"&gt;
      &lt;PROCESO PERIODO="201603" TT="MN" PAC="9999" /&gt;
      &lt;SECCION ID="SADMIN "&gt;
        &lt;CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/06/2015"&gt;01/06/2015&lt;/CAMPO&gt;
      &lt;/SECCION&gt;
    &lt;/EMPLEADO&gt;
  &lt;/EMPLEADOS&gt;
  </code></pre>
  
  <p>Expected output</p>
  
  <pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
  &lt;EMPLEADOS&gt;
    &lt;EMPLEADO TIPO="A" NUMERO="123"&gt;
      &lt;PROCESO PERIODO="201603" TT="MN" PAC="9999" /&gt;
       &lt;SECCION ID="ETACIV"&gt;
       &lt;CAMPO ID="ETA_ETCNOM" SEC=" " FECHA=" "&gt;abc&lt;/CAMPO&gt;
       &lt;/SECCION&gt;
       &lt;SECCION ID="SADMIN "&gt;
       &lt;CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/03/2015"&gt;01/03/2015&lt;/CAMPO&gt;
       &lt;CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/06/2015"&gt;01/06/2015&lt;/CAMPO&gt;
       &lt;/SECCION&gt;
     &lt;/EMPLEADO&gt;
  &lt;/EMPLEADOS&gt;
  </code></pre>
  
  <hr>
  
  <p>The merging on elements are the same as attributes ? Technically , I need all the documents to be merged on ID attribute of CAMPO .</p>
  
  <p>Any help greatly appreciated .</p>
  
  <p>Thanks
  Nag</p>
  
  <p>I tried the below code ; getting Premature end of file. error.</p>
  
  <pre><code>import java.util.Properties;
  import java.io.InputStream;
  import org.jdom.input.SAXBuilder;
  import org.jdom.Document;
  import org.jdom.Element;
  import org.jdom.xpath.XPath;
  import org.jdom.output.XMLOutputter;
  import groovy.util.slurpersupport.GPathResult;
  import groovy.xml.StreamingMarkupBuilder;
  
  
  for( int i = 0; i &lt; dataContext.getDataCount(); i++ ) {
    InputStream is = dataContext.getStream(i);
    Properties props = dataContext.getProperties(i);
  
  def xs = new XmlSlurper()
  def employee = xs.parse(is);
  String Encod = "UTF-8" ;
  HashMap&lt;String, GPathResult&gt; CampoMap = new HashMap&lt;String, GPathResult&gt;()
  
  employee.EMPLEADOS.EMPLEADO.PROCESO.SECCION.CAMPO.each {
      CampoMap["${it.@ID}"] = it
  }
  
  new StreamingMarkupBuilder().bind {
       mkp.xmlDeclaration(["version":"1.0", "encoding":"UTF-8"]);
      EMPLEADOS {
          EMPLEADO.PROCESO.SECCION.each {
              if (CampoMap["${it.@ID}"] != null) {
                  it.appendNode(CampoMap["${it.@id}"].sites)
              }
              out &lt;&lt; it
          }
      }
  } .writeTo(is.newWriter(Encod))
  }
    dataContext.storeStream(is, props);
  </code></pre>
  
  <p>The new code is </p>
  
  <pre><code>import groovy.util.XmlParser
  import groovy.xml.MarkupBuilder
  
  def parser = new XmlParser()
  def writer = new StringWriter()
  def builder = new MarkupBuilder(writer)
  
  for( int i = 0; i &lt; dataContext.getDataCount(); i++ ) {
    InputStream is = dataContext.getStream(i);
    Properties props = dataContext.getProperties(i);
  
  def mergedDocument = (0..&lt;dataContext.dataCount)
      .collect { XmlParser.parse(dataContext.getStream(it)) }
      .inject { nodeA, nodeB -&gt; merge(nodeA, nodeB) }
  
  
  builder.mkp.xmlDeclaration(version:'1.0', encoding:'UTF-8')
  builder.EMPLEADOS {
      doc1.EMPLEADO.each { empleado -&gt;
          EMPLEADO(empleado.attributes()) {
             empleado.PROCESO.each { proceso -&gt; 
                 PROCESO(proceso.attributes()) 
             }
  
             empleado.SECCION.each { seccion -&gt;
                 SECCION(seccion.attributes()) {
                     seccion.CAMPO.each { campo -&gt;
                         CAMPO(campo.attributes(), campo.value().head())
                     }
                 }
             }            
          }
      }
  }
  is = mergedDocument ;
  }
  
  
  /*
   * Category to simplify XML node comparisons.
   * Basically, two Nodes are equal if their attributes are the same.
   */
  // class NodeCategory {
  //    static boolean equals(Node me, Node other) {
  //        me.attributes() == other.attributes()
  //    }
  
  //    static boolean isCase(List&lt;Node&gt; nodes, Node other) {
  //      nodes.find { it == other } != null
  //    }
  //}
  
  /*
   * Merges document b into document a.
   * WARNING: This method is destructive; it modifies document a
   * @Returns a, for convenience
   */
  def merge(a, b) {
  //    use(NodeCategory) {
          b.EMPLEADO.each { empleado -&gt;
              def existingEmpleado = a.EMPLEADO.find { 
                  it == empleado
              }
  
              if(existingEmpleado) {
                  // Empleado already exists, must merge differences.
  
                  // Add any missing PROCESO nodes.
                  empleado.PROCESO
                     .findAll { !(it in existingEmpleado.PROCESO) }
                     .with {
                         delegate.each { existingEmpleado.append(it) }
                     }
  
                  // Add any missing SECCION nodes.
                  empleado.SECCION
                     .findAll { !(it in existingEmpleado.SECCION) }
                     .with {
                         delegate.each { existingEmpleado.append(it) }
                     }
  
                  // Add any missing CAMPO nodes.
                  empleado.SECCION.each { seccion -&gt;
                      existingEmpleado.SECCION
                          .find { it == seccion }
                          .with {
                              seccion.CAMPO
                                  .findAll { !(it in delegate.CAMPO) }
                                  .each { delegate.append(it) }
                          }
                  }
              } else {
                  // Empleado does not exist, go ahead and add it as-is.
                  a.append(empleado)
              }
          }    
   //   }
  
      return a
  }
  </code></pre>