2010-05-05 19 views
7

Como el título lo dice, tengo un archivo XML enorme (GB)¿Cómo transformar enormes archivos xml en Java?

<root> 
<keep> 
    <stuff> ... </stuff> 
    <morestuff> ... </morestuff> 
</keep> 
<discard> 
    <stuff> ... </stuff> 
    <morestuff> ... </morestuff> 
</discard> 
</root> 

y me gustaría para transformarlo en un ser mucho más pequeño que sólo conserva algunos de los elementos.
Mi analizador debe hacer lo siguiente:
1. Revisar el archivo hasta que comience un elemento relevante.
2. Copie todo el elemento relevante (con elementos secundarios) en el archivo de salida. vaya a 1.

paso 1 es fácil con SAX e imposible para los analizadores DOM.
paso 2 es molesto con SAX, pero fácil con el DOM-Analizador o XSLT.

¿y qué? - ¿Existe una forma ordenada de combinar SAX y DOM-Parser para realizar la tarea?

+1

Creo que VTD-XML extendido es ideal para eso, es compatible con la carga parcial de documentos (a través del mapa mem) y XPath ... cortar y pegar es muy superior a DOM o SAX –

Respuesta

9

Sí, solo escribe un controlador de contenido SAX, y cuando se encuentra con un elemento determinado, creas un árbol dom en ese elemento. He hecho esto con archivos muy grandes, y funciona muy bien.

En realidad es muy fácil: tan pronto como se encuentra con el inicio del elemento que desea, establece un indicador en su controlador de contenido, y a partir de ahí, reenvía todo al generador de DOM. Cuando encuentre el final del elemento, establezca el indicador en falso y escriba el resultado.

(Para los casos más complejos con elementos anidados del mismo nombre del elemento, que necesitará para crear una pila o un contador, pero que aún así es muy fácil de hacer.)

10

StAX parece ser una solución obvia: es un analizador de extracción en lugar del enfoque de "empuje" de SAX o de "amortiguar todo" de DOM. Sin embargo, no puedo decir que lo haya usado. Un "StAX tutorial" search puede ser útil :)

+1

+1 StAX es mucho más fácil de usar que SAX si no ha estado expuesto a manejar archivos XML antes. Además, también permite escribir XML (a diferencia de SAX). – helpermethod

0

Puede hacerlo fácilmente con XMLEventReader y varios XMLEventWriter s del paquete javax.xml.stream.

3

Como estás hablando de GB, prefiero priorizar el uso de memoria en la consideración. SAX necesita aproximadamente 2 veces más de memoria que el documento grande, mientras que DOM necesita que sea al menos 5 veces. Entonces, si su archivo XML tiene un tamaño de 1 GB, DOM requerirá un mínimo de 5 GB de memoria libre. Eso ya no es gracioso. Entonces SAX (o cualquier variante sobre él, como StAX) es la mejor opción aquí.

Si desea el enfoque más eficiente de la memoria, consulte VTD-XML. Solo requiere un poco de más memoria que el archivo grande.

+0

Buen punto, la memoria es absolutamente crucial aquí. BTW, SAX ni siquiera necesita necesariamente el doble del tamaño del documento, ya que es una API de transmisión, puede recoger constantemente elementos anteriores del documento, tan pronto como ya no los necesite. –

+0

Es cierto, pero eso depende de los requisitos funcionales. Por ejemplo, podría requerir tener todo el XML en memoria antes de poder recopilar la información deseada. – BalusC

2

Para un documento XML tan grande, algo con una arquitectura de transmisión, como Omnimark sería ideal.

No tendría que ser nada complejo tampoco. Una secuencia de comandos Omnimark gusta lo que está por debajo de usted podría darle lo que necesita:

process 

submit #main-input 

macro upto (arg string) is 
    ((lookahead not string) any)* 
macro-end 

find (("<keep") upto ("</keep>") "</keep>")=>keep 
    output keep 

find any 
5

Hice buenas experiencias con STX (Streaming transformaciones para XML).Básicamente, es una versión transmitida de XSLT, muy adecuada para analizar grandes cantidades de datos con una huella de memoria mínima. Tiene una implementación en Java llamada Joost.

Debería ser fácil encontrar una transformación STX que ignore todos los elementos hasta que el elemento coincida con un XPath determinado, copie ese elemento y todos sus elementos secundarios (utilizando una plantilla de identidad dentro de un grupo de plantillas) y continúe ignorando elementos hasta el próximo partido.

ACTUALIZACIÓN

I hackeado una STX transformar que hace lo que entiendo que desea. En su mayoría, depende de características solo STX como grupos de plantillas y plantillas predeterminadas configurables.

<stx:transform xmlns:stx="http://stx.sourceforge.net/2002/ns" 
    version="1.0" pass-through="none" output-method="xml"> 
    <stx:template match="element/child"> 
     <stx:process-self group="copy" /> 
    </stx:template> 
    <stx:group name="copy" pass-through="all"> 
    </stx:group> 
</stx:transform> 

El pass-through="none" en el stx:transform configura las plantillas predeterminadas (por nodos, atributos etc.) para producir ninguna salida, pero los elementos proceso hijo. Entonces el stx:template coincide con el XPath element/child (este es el lugar donde pone su expresión de coincidencia), se "procesa a sí mismo" en el grupo "copiar", lo que significa que la plantilla correspondiente del group name="copy" se invoca en el elemento actual. Ese grupo tiene pass-though="all", por lo que las plantillas predeterminadas copian su entrada y procesan elementos secundarios. Cuando finaliza el elemento element/child, el control se devuelve a la plantilla que invoca process-self, y los siguientes elementos se ignoran nuevamente. Hasta que la plantilla coincida nuevamente.

El siguiente es un ejemplo de archivo de entrada:

<root> 
    <child attribute="no-parent, so no copy"> 
    </child> 
    <element id="id1"> 
     <child attribute="value1"> 
      text1<b>bold</b> 
     </child> 
    </element> 
    <element id="id2"> 
     <child attribute="value2"> 
      text2 
      <x:childX xmlns:x="http://x.example.com/x"> 
      <!-- comment --> 
       yet more<b i="i" x:i="x-i" ></b> 
      </x:childX> 
     </child> 
    </element> 
</root> 

Este es el archivo de salida correspondiente:

<?xml version="1.0" encoding="UTF-8"?> 
<child attribute="value1"> 
      text1<b>bold</b> 
     </child><child attribute="value2"> 
      text2 
      <x:childX xmlns:x="http://x.example.com/x"> 
      <!-- comment --> 
       yet more<b i="i" x:i="x-i" /> 
      </x:childX> 
     </child> 

El formato inusual es el resultado de saltarse los nodos de texto que contienen saltos de línea fuera de los child elementos .

+0

suena bien. ¿Puedo simplemente escribir una xslt-stylesheet y luego ejecutarla con STX? – user306708

+0

No, esto no es posible. Si bien XSLT usa modos para distinguir las plantillas para la misma coincidencia en diferentes situaciones (modo de omisión frente a modo de copia, en su caso), STX usa grupos de plantillas. La sintaxis dentro de las plantillas es similar a XSLT, pero diferente en los detalles. Agrego un ejemplo de transformación a mi respuesta. –

+0

Tenga en cuenta que, en XPath para hacer coincidir una plantilla, los únicos nodos a los que puede acceder son el nodo actual, sus nodos principales y sus atributos. No puede coincidir con ningún otro nodo anterior o siguiente, debido a la naturaleza de transmisión de la transformación. Si necesita este tipo de coincidencia, puede definir variables (que son mutables) y usarlas en las pruebas 'stx: if'. Pero esto es complicado y se siente como implementar un controlador de contenido en XML. –

Cuestiones relacionadas