2009-08-24 14 views
7

Tengo un archivo que consiste en documentos XML válidos concatenados. Me gustaría separar documentos XML individuales de manera eficiente.Cómo analizar eficientemente documentos XML concatenados desde un archivo

El contenido del archivo concatenado se verá así, por lo que el archivo concatenado no es en sí mismo un documento XML válido.

<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 

Cada documento XML individual alrededor de 1-4 KB, pero hay potencialmente unos cientos de ellos. Todos los documentos XML corresponden al mismo Esquema XML.

¿Alguna sugerencia o herramienta? Estoy trabajando en el entorno de Java.

Editar: No estoy seguro de si la declaración xml estará presente en los documentos o no.

Editar: Supongamos que la codificación para todos los documentos xml es UTF-8.

+1

¿Suponemos que la codificación de caracteres sigue siendo la misma para cada uno? De lo contrario, esto se vuelve mucho más difícil :-) –

+0

Todos los archivos usan la misma codificación que el documento en sí. No importa si dicen que son UTF-8. Si el documento concatenado tiene el formato UTF-16, todos son UTF-16. –

Respuesta

3

Como dice Eamon, si conoces la cosa <? Xml > siempre estará ahí, solo rompe eso.

De lo contrario, busque la etiqueta de nivel de documento final. Es decir, escanea el texto contando cuántos niveles tienes profundos. Cada vez que vea una etiqueta que comienza con "<" pero no "< /" y que no termina con "/ >", agregue 1 a la cuenta de profundidad. Cada vez que vea una etiqueta que empiece por "< /", reste 1. Cada vez que resta 1, compruebe si ahora está en cero. Si es así, ha llegado al final de un documento XML.

+0

¿Por qué no solo busca? – wds

+0

Y nuevamente, ¿por qué no eliminar las instrucciones de procesamiento en su lugar, agregando todo lo demás en una etiqueta más grande? La instrucción de procesamiento ya no es útil, ya que todos los archivos usan la misma codificación que el documento grande. Con ellos desaparecidos, incluir una superetiqueta simplemente lo convierte en XML válido de nuevo. –

+0

Depende de cuál es el requisito final. La pregunta fue expresada como, ¿cómo los divido ?, entonces eso es lo que estaba tratando de responder.Sin saber lo que el póster original está tratando de hacer con la salida, no sé si envolverlos todos en una etiqueta grande es una solución viable o no. Si es así, genial, ve por ello. Puede haber otras soluciones potenciales en esa dirección. Al igual que si todos los archivos comparten una etiqueta de nivel superior común, tal vez podría combinarlos todos en una única etiqueta, es decir, quitar las etiquetas de inicio en todas las etiquetas salvo la primera y la final en todas menos la última. – Jay

3

Puesto que usted no está seguro de la declaración estará siempre presente, se puede despojar a todas las declaraciones (una expresión regular como <\?xml version.*\?> puede encontrar estos), anteponga <doc-collection>, anexar </doc-collection>, de manera que la cadena resultante será un documento XML válido . En él, puede recuperar los documentos por separado utilizando (por ejemplo) la consulta XPath /doc-collection/*. Si el archivo combinado puede ser lo suficientemente grande como para que el consumo de memoria se convierta en un problema, es posible que deba utilizar un analizador de transmisión como Sax, pero el principio sigue siendo el mismo.

En un escenario similar que me encontré, simplemente leer el documento concatenados directamente a través de un xml-parser: Aunque el archivo concatenado puede no ser un documento XML válida, es un XML válido fragmento (salvo el declaraciones repetidas) - entonces, una vez que quites las declaraciones, si tu analizador admite el análisis de fragmentos, entonces también puedes simplemente leer el resultado directamente. Todos los elementos de nivel superior serán entonces los elementos raíz de los documentos concatenados.

En resumen, si elimina todas las declaraciones, tendrá un fragmento xml válido que se puede analizar trivialmente directamente o rodeándolo con alguna etiqueta.

4

Do not split! ¡Agregue una etiqueta grande alrededor de esto! Entonces se convierte en un archivo XML nuevo:

<BIGTAG> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
</BIGTAG> 

Ahora, utilizando/BIGTAG/somedata le dará todas las raíces XML.


Si las instrucciones de procesamiento están en el camino, siempre puede usar un RegEx para eliminarlas. Es más fácil eliminar todas las instrucciones de procesamiento que usar un RegEx para encontrar todos los nodos raíz. Si la codificación es diferente para todos los documentos, recuerde esto: todo el documento en sí debe haber sido codificado por algún tipo de codificación, por lo tanto, todos los documentos XML que incluye usarán la misma codificación, independientemente de lo que le indique cada encabezado. Si el archivo grande está codificado como UTF-16, entonces no importa si las instrucciones de procesamiento XML dicen que el XML en sí es UTF-8. No será UTF-8 ya que todo el archivo es UTF-16. La codificación en esas instrucciones de procesamiento XML no es válida.

Al fusionar en un solo archivo, se ha modificado la codificación ...


Por RegEx, me refiero a las expresiones regulares. Solo tiene que eliminar todo el texto que está entre <? y a? > que no debería ser demasiado difícil con una expresión regular y algo más complicado si estás probando otras técnicas de manipulación de cadenas.

+1

Las instrucciones de procesamiento que comienzan con "xml" o "XML" están reservadas para estándares XML, por lo que su uso como PI "personalizados" como este no es realmente válido. –

+0

Al menos el analizador XML de Firefox no le gustó esto ... –

+0

Creo que esto es correcto en gran medida aparte de las instrucciones de procesamiento –

0

No tengo una respuesta de Java, pero así es como resolví este problema con C#.

he creado una clase llamada XmlFileStreams para escanear el documento fuente para la declaración de documento XML y dividirlo lógicamente en múltiples documentos:

class XmlFileStreams { 

    List<int> positions = new List<int>(); 
    byte[] bytes; 

    public XmlFileStreams(string filename) { 
     bytes = File.ReadAllBytes(filename); 
     for (int pos = 0; pos < bytes.Length - 5; ++pos) 
      if (bytes[pos] == '<' && bytes[pos + 1] == '?' && bytes[pos + 2] == 'x' && bytes[pos + 3] == 'm' && bytes[pos + 4] == 'l') 
       positions.Add(pos); 
     positions.Add(bytes.Length); 
    } 

    public IEnumerable<Stream> Streams { 
     get { 
      if (positions.Count > 1) 
       for (int i = 0; i < positions.Count - 1; ++i) 
        yield return new MemoryStream(bytes, positions[i], positions[i + 1] - positions[i]); 
     } 
    } 

} 

Para utilizar XmlFileStreams:

foreach (Stream stream in new XmlFileStreams(@"c:\tmp\test.xml").Streams) { 
    using (var xr = XmlReader.Create(stream, new XmlReaderSettings() { XmlResolver = null, ProhibitDtd = false })) { 
     // parse file using xr 
    } 
} 

Hay una un par de advertencias.

  1. Lee todo el archivo en la memoria para su procesamiento. Esto podría ser un problema si el archivo es realmente grande.
  2. Utiliza una simple búsqueda de fuerza bruta para buscar los límites del documento XML.
1

Esta es mi respuesta para la versión C#. código muy feo que funciona: - \

public List<T> ParseMultipleDocumentsByType<T>(string documents) 
    { 
     var cleanParsedDocuments = new List<T>(); 
     var serializer = new XmlSerializer(typeof(T)); 
     var flag = true; 
     while (flag) 
     { 
      if(documents.Contains(typeof(T).Name)) 
      { 
       var startingPoint = documents.IndexOf("<?xml"); 
       var endingString = "</" +typeof(T).Name + ">"; 
       var endingPoing = documents.IndexOf(endingString) + endingString.Length; 
       var document = documents.Substring(startingPoint, endingPoing - startingPoint); 
       var singleDoc = (T)XmlDeserializeFromString(document, typeof(T)); 
       cleanParsedDocuments.Add(singleDoc); 
       documents = documents.Remove(startingPoint, endingPoing - startingPoint); 
      } 
      else 
      { 
       flag = false; 
      } 
     } 


     return cleanParsedDocuments; 
    } 

    public static object XmlDeserializeFromString(string objectData, Type type) 
    { 
     var serializer = new XmlSerializer(type); 
     object result; 

     using (TextReader reader = new StringReader(objectData)) 
     { 
      result = serializer.Deserialize(reader); 
     } 

     return result; 
    } 
Cuestiones relacionadas