XML组合文档

时间:2016-03-02 03:29:55

标签: groovy

我正在研究Boomi界面,我需要将单个xml文档合并到单个输出文档中。标准合并文档步骤无法正常工作。

所有xml文档都具有相同的结构。

第一份文件

<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
    <SECCION ID="ETACIV">
      <CAMPO ID="ETA_ETCNOM" SEC=" " FECHA=" ">abc</CAMPO>
    </SECCION>
  </EMPLEADO>
</EMPLEADOS>

第二份文件

<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
    <SECCION ID="SADMIN ">
      <CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/03/2015">01/03/2015</CAMPO>
    </SECCION>
  </EMPLEADO>
</EMPLEADOS>

第三份文件

<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
    <SECCION ID="SADMIN ">
      <CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/06/2015">01/06/2015</CAMPO>
    </SECCION>
  </EMPLEADO>
</EMPLEADOS>

预期输出

<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
     <SECCION ID="ETACIV">
     <CAMPO ID="ETA_ETCNOM" SEC=" " FECHA=" ">abc</CAMPO>
     </SECCION>
     <SECCION ID="SADMIN ">
     <CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/03/2015">01/03/2015</CAMPO>
     <CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/06/2015">01/06/2015</CAMPO>
     </SECCION>
   </EMPLEADO>
</EMPLEADOS>

元素的合并与属性相同?从技术上讲,我需要将所有文档合并到CAMPO的ID属性上。

非常感谢任何帮助。

由于 纳格

我尝试了以下代码;得到过早的文件结束。错误。

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 < 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<String, GPathResult> CampoMap = new HashMap<String, GPathResult>()

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 << it
        }
    }
} .writeTo(is.newWriter(Encod))
}
  dataContext.storeStream(is, props);

新代码

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 < dataContext.getDataCount(); i++ ) {
  InputStream is = dataContext.getStream(i);
  Properties props = dataContext.getProperties(i);

def mergedDocument = (0..<dataContext.dataCount)
    .collect { XmlParser.parse(dataContext.getStream(it)) }
    .inject { nodeA, nodeB -> merge(nodeA, nodeB) }


builder.mkp.xmlDeclaration(version:'1.0', encoding:'UTF-8')
builder.EMPLEADOS {
    doc1.EMPLEADO.each { empleado ->
        EMPLEADO(empleado.attributes()) {
           empleado.PROCESO.each { proceso -> 
               PROCESO(proceso.attributes()) 
           }

           empleado.SECCION.each { seccion ->
               SECCION(seccion.attributes()) {
                   seccion.CAMPO.each { campo ->
                       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<Node> 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 ->
            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 ->
                    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
}

1 个答案:

答案 0 :(得分:2)

首先,我应该提到组合 XML文档的通用方法是不可能的,因为合并过程是上下文的。 XML节点的合并方式取决于节点的含义。计算机无法确定数据的含义,因此您必须像程序员一样提供说明。话虽如此,这里是如何合并您的 XML文档。

import groovy.util.XmlParser
import groovy.xml.MarkupBuilder

def parser = new XmlParser()
def writer = new StringWriter()
def builder = new MarkupBuilder(writer)

def doc1 = parser.parseText('''<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
    <SECCION ID="ETACIV">
      <CAMPO ID="ETA_ETCNOM" SEC=" " FECHA=" ">abc</CAMPO>
    </SECCION>
  </EMPLEADO> 
</EMPLEADOS>''')

def doc2 = parser.parseText('''<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
    <SECCION ID="SADMIN ">
      <CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/03/2015">01/03/2015</CAMPO>
    </SECCION>
  </EMPLEADO>
</EMPLEADOS>''')

def doc3 = parser.parseText('''<?xml version='1.0' encoding='UTF-8'?>
<EMPLEADOS>
  <EMPLEADO TIPO="A" NUMERO="123">
    <PROCESO PERIODO="201603" TT="MN" PAC="9999" />
    <SECCION ID="SADMIN ">
      <CAMPO ID="SAD_SADESO" SEC=" " FECHA="01/06/2015">01/06/2015</CAMPO>
    </SECCION>
  </EMPLEADO>
</EMPLEADOS>''')


merge(doc1, doc2)
merge(doc1, doc3)

builder.mkp.xmlDeclaration(version:'1.0', encoding:'UTF-8')
builder.EMPLEADOS {
    doc1.EMPLEADO.each { empleado ->
        EMPLEADO(empleado.attributes()) {
           empleado.PROCESO.each { proceso -> 
               PROCESO(proceso.attributes()) 
           }

           empleado.SECCION.each { seccion ->
               SECCION(seccion.attributes()) {
                   seccion.CAMPO.each { campo ->
                       CAMPO(campo.attributes(), campo.value().head())
                   }
               }
           }            
        }
    }
}

println writer

/*
 * 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<Node> 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 ->
            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 ->
                    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
}

过程如下:

  1. merge(Node a, Node b)方法遍历节点,处理每个案例,以便a最终成为两个文档(节点树)的组合。它基于确定b中的节点是否已经在a中。如果不是,则按原样添加节点。否则,相应地合并更改a。是的,这种方法很难看,是一个真正的PITA。请为了声音编程,重构野兽。
  2. 最后,MarkupDocumentBuilder用于处理最终节点树并生成序列化的XML文档。
  3. 您可能会注意到涉及到Groovy类别。它用于简化Node比较。

    附录 - 使用输入流

    您可以使用InputStream作为XML文档的源来调用相同的进程。它会是这样的:

    def parser = new XmlParser()
    def mergedDocument = (0..<dataContext.dataCount)
        .collect { parser.parse(dataContext.getStream(it) }
        .inject { nodeA, nodeB -> merge(nodeA, nodeB) }
    

    然后,您可以使用mergedDocument处理MarkupBuilder