Comment puis-je diviser un document XML en tiers (ou, mieux encore, en n morceaux) ?

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

  •  08-06-2019
  •  | 
  •  

Question

J'aimerais utiliser un langage que je connais - Java, C#, Ruby, PHP, C/C++, bien que des exemples dans n'importe quel langage ou pseudocode soient plus que bienvenus.

Quelle est la meilleure façon de diviser un gros document XML en sections plus petites qui restent du XML valide ?Pour mes besoins, je dois les diviser en tiers ou en quarts environ, mais pour donner des exemples, il serait bon de les diviser en n composants.

Était-ce utile?

La solution

Bien sûr, vous pouvez toujours extraire les éléments de niveau supérieur (c'est à vous de décider si c'est la granularité que vous souhaitez).En C#, vous utiliserez la classe XmlDocument.Par exemple, si votre fichier XML ressemble à ceci :

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

alors vous utiliseriez un code comme celui-ci pour extraire toutes les pièces :

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
}

Une fois que vous avez les nœuds, vous pouvez faire quelque chose avec eux dans votre code, ou vous pouvez transférer l'intégralité du texte du nœud vers son propre document XML et agir sur celui-ci comme s'il s'agissait d'un élément XML indépendant (y compris en l'enregistrant). retour sur le disque, etc.).

Autres conseils

L'analyse de documents XML à l'aide de DOM n'est pas évolutive.

Ce Sensationnel-script utilise StAX (Streaming API for XML) pour diviser un document XML entre les éléments de niveau supérieur (qui partagent le même QName que le premier enfant du document racine).C'est assez rapide, gère des documents volumineux arbitraires et est très utile lorsque vous souhaitez diviser un gros fichier batch en morceaux plus petits.

Nécessite Groovy sur Java 6 ou une API StAX et une implémentation telle que Woodstox dans le CHEMIN DE CLASSE

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

Comme DannySmurf l'évoque ici, tout dépend de la structure du document XML.
Si vous n'avez que deux énormes balises de "niveau supérieur", il sera extrêmement difficile de pouvoir les diviser de manière à permettre à la fois de les fusionner et de les lire morceau par morceau en tant que fichier XML valide.

Étant donné un document contenant de nombreux éléments séparés comme ceux de l'exemple de DannySmurfs, cela devrait être assez simple.
Un peu de code approximatif 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);
}

Cela devrait vous donner n documents avec le code XML correct et la possibilité de les fusionner à nouveau.
Mais encore une fois, cela dépend du fichier XML.

C'est plus un commentaire qu'une réponse, mais ce ne serait pas le cas :

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

Lire l'intégralité du fichier d'un coup ?Je pensais juste que je devrais soulever ce point car, d'après la question de Thomas, il est préoccupé par la lecture de fichiers volumineux et souhaite décomposer le processus.

Il lirait le fichier entier en une seule fois.D'après mon expérience, cependant, si vous lisez simplement le fichier, effectuez un traitement (c'est-à-dire le divisez) puis continuez votre travail, le XmlDocument va passer par son cycle de création/lecture/collecte si rapidement que cela n'aura probablement pas d'importance.

Bien sûr, cela dépend de ce qu'est un "gros" fichier.S'il s'agit d'un fichier XML de 30 Mo (ce que je considérerais comme volumineux pour un fichier XML), cela ne fera probablement aucune différence.S'il s'agit d'un fichier XML de 500 Mo, l'utilisation de XmlDocument deviendra extrêmement problématique sur les systèmes ne disposant pas d'une quantité importante de RAM (dans ce cas, cependant, je dirais que le temps nécessaire pour parcourir manuellement le fichier avec un XmlReader serait le plus important. obstacle).

Je ne sais pas quel type de traitement vous effectuez, mais pour le très gros XML, j'ai toujours été fan du traitement basé sur les événements.C'est peut-être mon expérience Java, mais j'aime vraiment SAX.Vous devez gérer votre propre état, mais une fois que vous avez dépassé cela, c'est une méthode très efficace pour analyser XML.

http://saxdotnet.sourceforge.net/

Je vais aller avec Youphoric sur celui-ci.Pour les fichiers très volumineux, SAX (ou tout autre analyseur de streaming) sera d'une grande aide dans le traitement.En utilisant DOM, vous pouvez collecter uniquement les nœuds de niveau supérieur, mais vous devez toujours analyser l'intégralité du document pour le faire... l'utilisation d'un analyseur de streaming et d'un traitement basé sur les événements vous permet de "sauter" les nœuds qui ne vous intéressent pas ;rend le traitement plus rapide.

Si vous n'êtes pas complètement allergique à Perl, alors XML :: Brindille est livré avec un outil nommé xml_split qui peut diviser un document, produisant une section XML bien formée.Vous pouvez diviser sur un niveau de l'arborescence, par taille ou sur une expression XPath.

Il semble que vous travaillez avec C# et .NET 3.5.J'ai rencontré des articles suggérant d'utiliser un algorithme de type rendement sur un flux de fichiers avec un XmlReader.

Voici quelques articles de blog pour vous aider à démarrer :

J'ai fait une vidéo YouTube montrant comment diviser des fichiers XML avec renard (l'éditeur XML gratuit de Premier objet) en utilisant seulement une petite quantité de mémoire, quelle que soit la taille des fichiers d'entrée et de sortie.

L'utilisation de la mémoire pour cette solution de lecteur XML CMarkup (analyseur pull) et d'écriture XML dépend de la taille des sous-documents qui sont individuellement transférés du fichier d'entrée vers les fichiers de sortie, ou de la taille de bloc minimale de 16 Ko.

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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top