Pregunta

Estoy tratando de leer un solo documento XML de la transmisión a la vez usando dom4j, procesarlo y luego pasar al siguiente documento de la transmisión. Desafortunadamente, SAXReader de dom4j (usando JAXP debajo de las cubiertas) sigue leyendo y se ahoga en el siguiente elemento del documento.

¿Hay alguna forma de hacer que SAXReader deje de leer la secuencia una vez que encuentra el final del elemento del documento? ¿Hay una mejor manera de lograr esto?

¿Fue útil?

Solución

Pude hacer que esto funcionara con algunas gimnasias usando algunas clases internas de JAXP:

  • Cree un escáner personalizado, una subclase de XMLNSDocumentScannerImpl
    • Cree un controlador personalizado, una implementación de XMLNSDocumentScannerImpl.Driver, dentro del escáner personalizado que devuelve END_DOCUMENT cuando ve una declaración o un elemento. Obtenga ScannedEntity de fElementScanner.getCurrentEntity (). Si la entidad tiene un PushbackReader, devuelva los caracteres no leídos restantes en el búfer de la entidad al lector.
    • En el constructor, reemplaza el fTrailingMiscDriver con una instancia de este controlador personalizado.
  • Cree una clase de configuración personalizada, una subclase de XIncludeAwareParserConfiguration, que reemplace el stock DOCUMENT_SCANNER con una instancia de este escáner personalizado en su constructor.
  • Instale una instancia de esta clase de configuración personalizada como " com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration " propiedad de modo que se instanciará cuando la clase SAXReader de dom4j intente crear un XMLReader JAXP.
  • Al pasar un lector al método SAXReader.read () de dom4j, proporcione un PushbackReader con un tamaño de búfer considerablemente mayor que el valor predeterminado de un carácter. Al menos 8192 debería ser suficiente para admitir el tamaño de búfer predeterminado del XMLEntityManager dentro de la copia de Apache2 de JAXP.

Esta no es la solución más limpia, ya que implica subclasificar las clases internas de JAXP, pero funciona.

Otros consejos

Lo más probable es que no desee tener más de un documento en la misma secuencia al mismo tiempo. No creo que el SAXReader sea lo suficientemente inteligente como para detenerse cuando llegue al final del primer documento. ¿Por qué es necesario tener varios documentos en la misma secuencia como esta?

Creo que tendrías que agregar un adaptador, algo para envolver la secuencia y hacer que esa cosa devuelva el final del archivo cuando vea el comienzo del próximo documento. Hasta donde yo sé, los analizadores escritos, irán hasta el final del archivo o un error ... y ver otro <?xml version="1.0"?> sin duda sería un error.

Suponiendo que usted es responsable de colocar documentos en la secuencia en primer lugar, debería ser fácil delimitar los documentos de alguna manera. Por ejemplo:

// Any value that is invalid for an XML character will do.
static final char DOC_TERMINATOR=4;

BOOL addDocumentToStream(BufferedWriter streamOut, char xmlData[])
{
  streamOut.write(xmlData);
  streamOut.write(DOC_TERMINATOR);
}

Luego, al leer desde la secuencia, lea en una matriz hasta que se encuentre DOC_TERMINATOR.

char *getNextDocuument(BufferedReader streamIn)
{
  StringBuffer buffer = new StringBuffer();
  int character;

  while (true)
  {
    character = streamIn.read();
    if (character == DOC_TERMINATOR)
      break;

    buffer.append(character);
  }
  return buffer.toString().toCharArray();
}

Dado que 4 es un valor de carácter no válido que no encontrará, excepto donde lo agregue explícitamente. Por lo tanto, le permite dividir los documentos. Ahora simplemente envuelva el conjunto de caracteres resultante para ingresarlo en SAX y listo.

...
  XMLReader xmlReader = XMLReaderFactory.createXMLReader();
...
  while (true)
  {
    char xmlDoc = getNextDocument(streamIn);

    if (xmlDoc.length == 0)
      break;

    InputSource saxInputSource = new InputSource(new CharArrayReader(xmlDoc));
    xmlReader.parse(saxInputSource);
  }
...

Tenga en cuenta que el ciclo termina cuando obtiene un documento de longitud 0. Esto significa que debe agregar un segundo DOC_TERMINATOR después del último documento que necesita para agregar algo para detectar el final de la secuencia en getNextDocument ().

He hecho esto antes envolviendo el lector base con otro lector de mi propia creación que tenía una capacidad de análisis muy simple. Suponiendo que conoce la etiqueta de cierre del documento, el contenedor simplemente analiza una coincidencia, p. para " < / MyDocument > " ;. Cuando detecta que devuelve EOF. La envoltura se puede adaptar adaptando la primera etiqueta de apertura y devolviendo EOF en la etiqueta de cierre correspondiente. Descubrí que no era necesario detectar el nivel de la etiqueta de cierre ya que en ningún documento había usado la etiqueta del documento dentro de sí mismo, por lo que se garantizó que la primera aparición de la etiqueta de cierre finalizó el documento.

Como recuerdo, uno de los trucos fue cerrar el bloque de envoltura (), ya que el lector DOM cierra la fuente de entrada.

Entonces, dada la entrada del lector, su código podría verse así:

SubdocReader sdr=new SubdocReader(input);
while(!sdr.eof()) {
    sdr.next();
    // read doc here using DOM
    // then process document
    }
input.close();

El método eof () devuelve verdadero si se encuentra EOF. El método next () marca al lector para que deje de devolver -1 para read ().

Espero que esto te indique una dirección útil.

- Kiwi.

Leería la secuencia de entrada en un búfer interno. Dependiendo del tamaño de flujo total esperado, leería todo el flujo y luego lo analizaría o detectaría el límite entre un xml y el siguiente (busque

La única diferencia real entre manejar una secuencia con un xml y una secuencia con múltiples xmls es el búfer y la lógica dividida.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top