¿Cómo puedo dividir un documento XML en tercios (o, mejor aún, n partes)?

StackOverflow https://stackoverflow.com/questions/7846

  •  08-06-2019
  •  | 
  •  

Pregunta

Me gustaría utilizar un lenguaje con el que esté familiarizado: Java, C#, Ruby, PHP, C/C++, aunque los ejemplos en cualquier lenguaje o pseudocódigo son más que bienvenidos.

¿Cuál es la mejor manera de dividir un documento XML grande en secciones más pequeñas que aún son XML válidas?Para mis propósitos, necesito dividirlos aproximadamente en tercios o cuartos, pero para proporcionar ejemplos, sería bueno dividirlos en n componentes.

¿Fue útil?

Solución

Bueno, por supuesto, siempre puedes extraer los elementos de nivel superior (depende de ti si esta es la granularidad que deseas).En C#, usarías la clase XmlDocument.Por ejemplo, si su archivo XML se parecía a esto:

<Document>
  <Piece>
     Some text
  </Piece>
  <Piece>
     Some other text
  </Piece>
</Document>

entonces usarías un código como este para extraer todas las Piezas:

XmlDocument doc = new XmlDocument();
doc.Load("<path to xml file>");
XmlNodeList nl = doc.GetElementsByTagName("Piece");
foreach (XmlNode n in nl)
{
    // Do something with each Piece node
}

Una vez que tenga los nodos, puede hacer algo con ellos en su código, o puede transferir el texto completo del nodo a su propio documento XML y actuar en consecuencia como si fuera una pieza independiente de XML (incluido guardarlo). volver al disco, etc.).

Otros consejos

El análisis de documentos XML utilizando DOM no escala.

Este maravilloso-script utiliza StAX (API de transmisión para XML) para dividir un documento XML entre los elementos de nivel superior (que comparten el mismo QName que el primer hijo del documento raíz).Es bastante rápido, maneja documentos grandes arbitrarios y es muy útil cuando desea dividir un archivo por lotes grande en partes más pequeñas.

Requiere Groovy en Java 6 o una API StAX e implementación como maderastox en el CLASSPATH

import javax.xml.stream.*

pieces = 5
input = "input.xml"
output = "output_%04d.xml"
eventFactory = XMLEventFactory.newInstance()
fileNumber = elementCount = 0

def createEventReader() {
    reader = XMLInputFactory.newInstance().createXMLEventReader(new FileInputStream(input))
    start = reader.next()
    root = reader.nextTag()
    firstChild = reader.nextTag()
    return reader
}

def createNextEventWriter () {
    println "Writing to '${filename = String.format(output, ++fileNumber)}'"
    writer = XMLOutputFactory.newInstance().createXMLEventWriter(new FileOutputStream(filename), start.characterEncodingScheme)
    writer.add(start)
    writer.add(root)
    return writer
}

elements = createEventReader().findAll { it.startElement && it.name == firstChild.name }.size()
println "Splitting ${elements} <${firstChild.name.localPart}> elements into ${pieces} pieces"
chunkSize = elements / pieces
writer = createNextEventWriter()
writer.add(firstChild)
createEventReader().each { 
    if (it.startElement && it.name == firstChild.name) {
        if (++elementCount > chunkSize) {
            writer.add(eventFactory.createEndDocument())
            writer.flush()
            writer = createNextEventWriter()
            elementCount = 0
        }
    }
    writer.add(it)
}
writer.flush()

Como menciona DannySmurf aquí, se trata de la estructura del documento xml.
Si solo tiene dos etiquetas enormes de "nivel superior", será extremadamente difícil poder dividirlas de una manera que permita volver a fusionarlas y leerlas pieza por pieza como xml válido.

Dado un documento con muchas piezas separadas como las del ejemplo de DannySmurfs, debería ser bastante fácil.
Algún código aproximado en Pseudo C#:

int nrOfPieces = 5;
XmlDocument xmlOriginal = some input parameter..

// construct the list we need, and fill it with XmlDocuments..
var xmlList = new List<XmlDocument>();
for (int i = 0; i < nrOfPieces ; i++)
{
    var xmlDoc = new XmlDocument();
    xmlDoc.ChildNodes.Add(new XmlNode(xmlOriginal.FistNode.Name));
    xmlList.Add(xmlDoc);
}

var nodeList = xmlOriginal.GetElementsByTagName("Piece")M
// Copy the nodes from the original into the pieces..
for (int i = 0; i < nodeList .Count; i++)
{
    var xmlDoc = xmlList[i % nrOfPieces];
    var nodeToCopy = nodeList[i].Clone();
    xmlDoc.FirstNode.ChildNodes.Add(nodeToCopy);
}

Esto debería brindarle n documentos con el xml correcto y la posibilidad de volver a fusionarlos.
Pero nuevamente, depende del archivo xml.

Esto es más un comentario que una respuesta, pero no sería así:

XmlDocument doc = new XmlDocument();
doc.Load("path");

¿Leer todo el archivo de una vez?Solo pensé que debería plantear el punto ya que, por lo que parece la pregunta de Thomas, a él le preocupa leer archivos grandes y quiere dividir el proceso.

Leería el archivo completo a la vez.Sin embargo, en mi experiencia, si simplemente estás leyendo el archivo, haciendo algo de procesamiento (es decir, dividiéndolo) y luego continuando con tu trabajo, el XmlDocument pasará por su ciclo de creación/lectura/recopilación tan rápido que probablemente no importe.

Por supuesto, eso depende de qué archivo sea "grande".Si se trata de un archivo XML de 30 MB (que yo consideraría grande para un archivo XML), probablemente no haga ninguna diferencia.Si se trata de un archivo XML de 500 MB, el uso de XmlDocument será extremadamente problemático en sistemas sin una cantidad significativa de RAM (en ese caso, sin embargo, yo diría que el tiempo para seleccionar manualmente el archivo con un XmlReader sería el más significativo). impedimento).

No estoy seguro de qué tipo de procesamiento estás realizando, pero para XML muy grande, siempre he sido un fanático del procesamiento basado en eventos.Tal vez sea mi experiencia en Java, pero realmente me gusta SAX.Necesita realizar su propia gestión del estado, pero una vez que lo supera, es un método muy eficaz para analizar XML.

http://saxdotnet.sourceforge.net/

Voy a optar por youphoric en este caso.Para archivos muy grandes, SAX (o cualquier otro analizador de transmisión) será de gran ayuda en el procesamiento.Usando DOM puedes recopilar solo nodos de nivel superior, pero aún tienes que analizar todo el documento para hacerlo... usar un analizador de transmisión y un procesamiento basado en eventos te permite "saltar" los nodos que no te interesan;hace que el procesamiento sea más rápido.

Si no es completamente alérgico a Perl, entonces XML::ramita viene con una herramienta llamada xml_split que puede dividir un documento, produciendo una sección XML bien formada.Puede dividir en un nivel del árbol, por tamaño o en una expresión XPath.

Parece que estás trabajando con C# y .NET 3.5.Me encontré con algunas publicaciones que sugieren usar un tipo de algoritmo de rendimiento en una secuencia de archivos con un XmlReader.

Aquí hay un par de publicaciones de blog para ayudarlo a comenzar el camino:

Hice un vídeo de YouTube que muestra cómo dividir archivos XML con zorro (el editor XML gratuito de primer objeto) utilizando solo una pequeña cantidad de memoria, independientemente del tamaño de los archivos de entrada y salida.

El uso de memoria para esta solución de lector XML (analizador de extracción) y escritor XML de CMarkup depende del tamaño de los subdocumentos que se transfieren individualmente desde el archivo de entrada a los archivos de salida, o el tamaño de bloque mínimo de 16 KB.

split()
{
  CMarkup xmlInput, xmlOutput;
  xmlInput.Open( "50MB.xml", MDF_READFILE );
  int nObjectCount = 0, nFileCount = 0;
  while ( xmlInput.FindElem("//ACT") )
  {
    if ( nObjectCount == 0 )
    {
      ++nFileCount;
      xmlOutput.Open( "piece" + nFileCount + ".xml", MDF_WRITEFILE );
      xmlOutput.AddElem( "root" );
      xmlOutput.IntoElem();
    }
    xmlOutput.AddSubDoc( xmlInput.GetSubDoc() );
    ++nObjectCount;
    if ( nObjectCount == 5 )
    {
      xmlOutput.Close();
      nObjectCount = 0;
    }
  }
  if ( nObjectCount )
    xmlOutput.Close();
  xmlInput.Close();
  return nFileCount;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top