Como posso dividir um documento XML em terços (ou, melhor ainda, n peças)?

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

  •  08-06-2019
  •  | 
  •  

Pergunta

Eu gostaria de usar uma linguagem que eu estou familiarizado com Java, C#, Ruby, PHP, C/C++, embora os exemplos em qualquer idioma ou pseudocódigo são mais do que bem-vindo.

Qual é a melhor maneira de dividir um documento XML grande em seções menores, que ainda são válidos XML?Para os meus propósitos, eu preciso dividi-los em cerca de terças ou quartas, mas por causa de fornecer exemplos, dividindo-as em n componentes seria bom.

Foi útil?

Solução

Bem, é claro que você sempre pode extrair os elementos de nível mais alto (se esta é a granularidade que você deseja é até você).Em C#, você pode usar a classe XmlDocument.Por exemplo, se seu arquivo XML parecia algo como isto:

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

então você gostaria de usar um código como este para extrair todas as Partes:

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
}

Uma vez que você tem a nós, você pode fazer algo com eles no seu código, ou você pode transferir todo o texto do nó a para o seu próprio documento XML e agimos como se fosse independente pedaço de XML (incluindo salvá-lo em disco, etc).

Outras dicas

A análise de documentos XML utilizando DOM não escala.

Este Groovy-script está usando StAX (Streaming API for XML) para dividir um documento XML entre os elementos de nível mais alto (que tem o mesmo QName como o primeiro filho da raiz do documento).É muito rápido, alças arbitrário documentos grandes e é muito útil quando você quer dividir um lote grande-arquivo em pedaços menores.

Requer Groovy no Java 6 ou um StAX de API e implementação, tais como Woodstox no 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 DannySmurf toca aqui, é tudo sobre a estrutura do documento xml.
Se você apenas dois enormes "nível superior" tags, que vai ser extremamente difícil de ser capaz de dividi-la em uma forma que torna possível tanto a série de volta e lê-lo, peça por peça, como xml válido.

Dado um documento com um monte de peças separadas como as DannySmurfs exemplo, deve ser bastante fácil.
Alguns áspero em Pseudo-código 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);
}

Este deve dar-lhe n docs com xml correto e a possibilidade de mesclar-los juntos novamente.
Mas, novamente, isso depende do arquivo xml.

Este é mais um comentário do que uma resposta, mas não:

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

Ler o arquivo inteiro de uma vez?Apenas pensei que eu deveria levantar a ponto, pois a partir do olhar de Thomas pergunta, ele está preocupado com a leitura de grandes arquivos e quer quebrar o processo para baixo..

Ele iria ler o arquivo inteiro de uma vez.Em minha experiência, porém, se você está apenas lendo o arquivo, fazer algum processamento (isto é, separá-lo) e, em seguida, continuar com o seu trabalho, o XmlDocument está indo para ir a criar/ler/recolher ciclo tão rapidamente que, provavelmente, não importa.

Claro, que depende de que um "grande" do arquivo é.Se é uma 30 MB de arquivo XML (que eu considero grande para um arquivo XML), o que provavelmente não fará nenhuma diferença.Se é uma 500 MB de arquivo XML, usando o XmlDocument tornou-se extremamente problemático em sistemas sem uma quantidade significativa de memória RAM (no caso, no entanto, eu diria que o tempo para escolher manualmente através do arquivo com um XmlReader seria o mais significativo de impedimento).

Não tem certeza qual o tipo de processamento que você está fazendo, mas para XML muito grande, eu sempre fui um fã de evento baseado no processamento.Talvez seja o meu Java fundo, mas eu realmente gosto do SAX.Você precisa fazer a sua própria gestão do estado, mas uma vez que você passar por isso, é um método muito eficiente de análise de XML.

http://saxdotnet.sourceforge.net/

Eu estou indo para ir com youphoric em um presente.Para arquivos muito grandes SAX (ou qualquer outro streaming parser) vai ser uma grande ajuda no processamento.Usando o DOM que você pode coletar apenas nós de nível superior, mas você ainda tem que analisar o documento inteiro para fazê-lo...usando um analisador de streaming e baseado em eventos de processamento permite que você "pule" a nós que você não está interessado em;torna o processamento mais rápido.

Se você não estiver completamente alérgica a Perl, em seguida, XML::Twig vem com uma ferramenta chamada xml_split que se pode dividir um documento, produzindo XML bem formado seção.Você pode dividir em um nível da árvore, por tamanho ou em uma expressão XPath.

Parece que você está trabalhando com C# e .NET 3.5.Eu vim através de alguns posts que sugerem o uso de um tipo de rendimento do algoritmo em um fluxo de arquivo com um XmlReader.

Eis aqui alguns posts do blog para você começar no caminho:

Eu fiz um vídeo do YouTube mostrando como dividir os arquivos XML com foxe (o livre editor de XML a partir de Firstobject) usando apenas uma pequena quantidade de memória, independentemente do tamanho dos arquivos de entrada e saída.

O uso de memória para este Depois leitor de XML (pull parser) e XML escritor solução depende do tamanho dos subdocumentos que individualmente são transferidos a partir do arquivo de entrada para os arquivos de saída, ou o mínimo tamanho de bloco 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top