parser parse node java xml sax xmlreader

parse - sax java xml



Analizar una secuencia XML sin elemento raĆ­z (6)

Necesito analizar un flujo continuo de elementos XML bien formados, a los que solo se me ha dado un objeto java.io.Reader ya construido. Estos elementos no están encerrados en un elemento raíz, ni están precedidos por un encabezado XML como <?xml version="1.0"?>" , Pero, por lo demás, son XML válidos.

El uso de la clase org.xml.sax.XMLReader Java no funciona, porque el Lector XML espera analizar XML bien formado, comenzando con un elemento raíz adjunto. Entonces, simplemente lee el primer elemento de la secuencia, que percibe como la raíz, y falla en la siguiente, con el típico

org.xml.sax.SAXParseException: El marcado en el documento que sigue al elemento raíz debe estar bien formado.

Para los archivos que no contienen un elemento raíz, pero donde tal elemento existe o se puede definir (y se llama, por ejemplo, MyRootElement), se puede hacer algo como lo siguiente:

Strint path = <the full path to the file>; XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); StringBuilder buffer = new StringBuilder(); buffer.append("<?xml version=/"1.0/"?>/n"); buffer.append("<!DOCTYPE MyRootElement "); buffer.append("[<!ENTITY data SYSTEM /"file:///"); buffer.append(path); buffer.append("/">]>/n"); buffer.append("<MyRootElement xmlns:...>/n"); buffer.append("&data;/n"); buffer.append("</MyRootElement>/n"); InputSource source = new InputSource(new StringReader(buffer.toString())); xmlReader.parse(source);

He probado lo anterior guardando parte de la salida de java.io.Reader en un archivo y funciona. Sin embargo, este enfoque no es aplicable en mi caso y dicha información adicional (encabezado XML, elemento raíz) no se puede insertar, ya que el objeto java.io.Reader pasado a mi código ya está construido.

Esencialmente, estoy buscando "análisis XML fragmentado". Entonces, mi pregunta es, ¿se puede hacer, utilizando las API estándar de Java (incluidos los org.sax.xml.* Y java.xml.* )?


La respuesta 3 funciona, pero para mí tuve que hacer el paso adicional de crear una fuente de entrada desde el SequenceInputStream.

XMLReader xmlReader = saxParser.getXMLReader(); xmlReader.setContentHandler((ContentHandler) this); // Trying to add root element Enumeration<InputStream> streams = Collections.enumeration( Arrays.asList(new InputStream[] { new ByteArrayInputStream("<TopNode>".getBytes()), new FileInputStream(xmlFile),//bogus xml new ByteArrayInputStream("</TopNode>".getBytes()), })); InputSource is = new InputSource(seqStream); xmlReader.parse(is);


Puede crear su propio Reader que delega en el Reader proporcionado, de esta forma:

final Reader reader = <whatever you are getting>; Reader wrappedReader = new Reader() { Reader readerCopy = reader; String start = "<?xml version=/"1.0/"?><MyRootElement>"; String end = "</MyRootElement>"; int index; @Override public void close() throws IOException { readerCopy.close(); } @Override public int read(char[] cbuf, int off, int len) throws IOException { // You''ll have to get the logic right here - this is only placeholder code if (index < start.length()) { // Copy from start to cbuf } int result = readerCopy.read(cbuf, off, len); if (result == -1) { // Copy from end } index += len; return result; } };

Tendrá que completar la lógica para leer primero desde el start , luego delegar al lector en el medio y, finalmente, cuando el lector esté vacío, lea desde el end .

Sin embargo, este enfoque funcionará.


Puede escribir su propia Implementación de Reader que encapsule la instancia de Reader que se le da. Este nuevo Reader debería hacer exactamente lo que está haciendo en su código de ejemplo, proporcionar el encabezado y el elemento raíz, luego los datos del lector subyacente y, al final, la etiqueta raíz de cierre. Al ir de esta manera, puede proporcionar una secuencia XML válida al analizador XML y también puede utilizar el objeto Reader que se pasa a su código.


Puedes envolver tu Reader en una subclase FilterReader que implementes para hacer más o menos lo que estás haciendo aquí.

Editar:

Si bien esto es similar a la propuesta de implementar su propio Reader delegando al objeto Reader dado por un par de otras respuestas, casi todos los métodos en FilterReader tendrían que ser anulados, por lo que es posible que no obtenga mucho al usar la superclase.

Una variación interesante de las otras propuestas podría ser implementar un SequencedReader que envuelva varios objetos de Reader y pase a la siguiente secuencia cuando se agote uno. Luego, puede pasar un objeto StringReader con el texto de inicio para la raíz que desea agregar, el Reader original y otro StringReader con la etiqueta de cierre.


SequenceInputStream viene al rescate:

SAXParserFactory saxFactory = SAXParserFactory.newInstance(); SAXParser parser = saxFactory.newSAXParser(); parser.parse( new SequenceInputStream( Collections.enumeration(Arrays.asList( new InputStream[] { new ByteArrayInputStream("<dummy>".getBytes()), new FileInputStream(file),//bogus xml new ByteArrayInputStream("</dummy>".getBytes()), })) ), new DefaultHandler() );


Solo inserte el elemento raíz ficticio. La solución más elegante en la que puedo pensar es crear su propio InputStream o Reader que envuelva InputSteam / Reader normal y devuelva el dummy <dummyroot> cuando llame a read () / readLine () por primera vez y luego devuelva el resultado del flujo de carga útil . Esto debería satisfacer el analizador de SAX.