Wie kann ich ein XML-Dokument in Drittel (oder noch besser in n Teile) aufteilen?

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

  •  08-06-2019
  •  | 
  •  

Frage

Ich würde gerne eine Sprache verwenden, mit der ich vertraut bin – Java, C#, Ruby, PHP, C/C++, obwohl Beispiele in jeder Sprache oder Pseudocode mehr als willkommen sind.

Wie lässt sich ein großes XML-Dokument am besten in kleinere Abschnitte aufteilen, die noch gültiges XML sind?Für meine Zwecke muss ich sie ungefähr in Drittel oder Viertel aufteilen, aber um Beispiele zu geben, wäre es gut, sie in n Komponenten aufzuteilen.

War es hilfreich?

Lösung

Natürlich können Sie jederzeit die Elemente der obersten Ebene extrahieren (ob dies die gewünschte Granularität ist, bleibt Ihnen überlassen).In C# würden Sie die XmlDocument-Klasse verwenden.Wenn Ihre XML-Datei beispielsweise so aussehen würde:

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

Dann würden Sie Code wie diesen verwenden, um alle Teile zu extrahieren:

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
}

Sobald Sie die Knoten haben, können Sie in Ihrem Code etwas damit machen oder den gesamten Text des Knotens in ein eigenes XML-Dokument übertragen und darauf reagieren, als wäre es ein unabhängiges XML-Stück (einschließlich Speichern). zurück auf die Festplatte usw.).

Andere Tipps

Das Parsen von XML-Dokumenten mithilfe von DOM lässt sich nicht skalieren.

Das Groovig-script verwendet StAX (Streaming API für XML), um ein XML-Dokument zwischen den Elementen der obersten Ebene aufzuteilen (die denselben QName wie das erste untergeordnete Element des Root-Dokuments haben).Es ist ziemlich schnell, verarbeitet beliebig große Dokumente und ist sehr nützlich, wenn Sie eine große Batch-Datei in kleinere Teile aufteilen möchten.

Erfordert Groovy auf Java 6 oder eine StAX-API und Implementierung wie z Woodstox im KLASSENPFAD

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()

Wie DannySmurf hier anspricht, dreht sich alles um die Struktur des XML-Dokuments.
Wenn Sie nur zwei große „Top-Level“-Tags haben, wird es äußerst schwierig sein, sie so aufzuteilen, dass sie sowohl wieder zusammengeführt als auch Stück für Stück als gültiges XML gelesen werden können.

Bei einem Dokument mit vielen einzelnen Teilen wie denen in DannySmurfs Beispiel sollte es ziemlich einfach sein.
Etwas grober Code in 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);
}

Dadurch sollten Sie n Dokumente mit korrektem XML erhalten und die Möglichkeit haben, diese wieder zusammenzuführen.
Aber auch hier kommt es auf die XML-Datei an.

Dies ist eher ein Kommentar als eine Antwort, würde aber nicht:

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

Die gesamte Datei auf einmal lesen?Ich dachte nur, ich sollte den Punkt ansprechen, da er, wie Thomas' Frage aussieht, Bedenken hat, große Dateien zu lesen, und den Prozess aufschlüsseln möchte.

Es würde die gesamte Datei auf einmal lesen.Meiner Erfahrung nach durchläuft das XmlDocument jedoch seinen Erstellungs-/Lese-/Sammelzyklus so schnell, wenn Sie die Datei nur lesen, etwas verarbeiten (d. h. aufteilen) und dann mit Ihrer Arbeit fortfahren es wird wahrscheinlich keine Rolle spielen.

Das hängt natürlich davon ab, was eine „große“ Datei ist.Wenn es sich um eine 30-MB-XML-Datei handelt (was ich für eine XML-Datei als groß bezeichnen würde), macht das wahrscheinlich keinen Unterschied.Wenn es sich um eine 500-MB-XML-Datei handelt, wird die Verwendung von XmlDocument auf Systemen ohne nennenswerte Menge an RAM äußerst problematisch (in diesem Fall würde ich jedoch argumentieren, dass die Zeit, die für das manuelle Durchsuchen der Datei mit einem XmlReader benötigt wird, umso bedeutender wäre). Behinderung).

Ich bin mir nicht sicher, welche Art von Verarbeitung Sie durchführen, aber bei sehr großen XML-Daten war ich schon immer ein Fan der ereignisbasierten Verarbeitung.Vielleicht liegt es an meinem Java-Hintergrund, aber ich mag SAX wirklich.Sie müssen Ihre eigene Statusverwaltung durchführen, aber sobald Sie darüber hinweg sind, ist dies eine sehr effiziente Methode zum Parsen von XML.

http://saxdotnet.sourceforge.net/

In diesem Fall schließe ich mich youphoric an.Bei sehr großen Dateien ist SAX (oder jeder andere Streaming-Parser) eine große Hilfe bei der Verarbeitung.Mit DOM können Sie nur Knoten der obersten Ebene erfassen, müssen dafür aber trotzdem das gesamte Dokument analysieren. Mithilfe eines Streaming-Parsers und einer ereignisbasierten Verarbeitung können Sie die Knoten „überspringen“, die Sie nicht interessieren.beschleunigt die Bearbeitung.

Wenn Sie nicht vollständig gegen Perl allergisch sind, dann XML::Twig kommt mit einem Werkzeug namens xml_split Dadurch kann ein Dokument aufgeteilt und ein wohlgeformter XML-Abschnitt erstellt werden.Sie können die Struktur auf einer Ebene, nach Größe oder nach einem XPath-Ausdruck aufteilen.

Es sieht so aus, als würden Sie mit C# und .NET 3.5 arbeiten.Ich bin auf einige Beiträge gestoßen, die die Verwendung eines Yield-Algorithmus für einen Dateistream mit einem XmlReader vorschlagen.

Hier sind ein paar Blogbeiträge, die Ihnen den Einstieg auf den Weg erleichtern sollen:

Ich habe ein YouTube-Video gemacht, das es zeigt wie man XML-Dateien aufteilt mit Fuchs (der kostenlose XML-Editor von Erstes Objekt) benötigt nur wenig Speicher, unabhängig von der Größe der Eingabe- und Ausgabedateien.

Die Speichernutzung für diese CMarkup-XML-Reader- (Pull-Parser) und XML-Writer-Lösung hängt von der Größe der Unterdokumente ab, die einzeln von der Eingabedatei in die Ausgabedateien übertragen werden, oder von der Mindestblockgröße von 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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top