Domanda

Sto provando a leggere un singolo documento XML dallo stream alla volta usando dom4j, elaborarlo, quindi passare al documento successivo sullo stream. Sfortunatamente, SAXReader di dom4j (usando JAXP sotto le copertine) continua a leggere e soffoca sul seguente elemento del documento.

Esiste un modo per impedire a SAXReader di interrompere la lettura dello stream una volta trovata la fine dell'elemento del documento? C'è un modo migliore per raggiungere questo obiettivo?

È stato utile?

Soluzione

Sono stato in grado di farlo funzionare con alcuni esercizi di ginnastica usando alcune classi JAXP interne:

  • Crea uno scanner personalizzato, una sottoclasse di XMLNSDocumentScannerImpl
    • Crea un driver personalizzato, un'implementazione di XMLNSDocumentScannerImpl.Driver, all'interno dello scanner personalizzato che restituisce END_DOCUMENT quando vede una dichiarazione o un elemento. Ottieni ScannedEntity da fElementScanner.getCurrentEntity (). Se l'entità ha un PushbackReader, rimanda sul lettore i caratteri non letti rimanenti nel buffer entità.
    • Nel costruttore, sostituisce fTrailingMiscDriver con un'istanza di questo driver personalizzato.
  • Crea una classe di configurazione personalizzata, una sottoclasse di XIncludeAwareParserConfiguration, che sostituisce lo stock DOCUMENT_SCANNER con un'istanza di questo scanner personalizzato nel suo costruttore.
  • Installa un'istanza di questa classe di configurazione personalizzata come " com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration " quindi verrà istanziata quando la classe SAXReader di dom4j tenta di creare un XMLReader JAXP.
  • Quando si passa un Reader al metodo SAXReader.read () di dom4j, fornire a PushbackReader una dimensione del buffer considerevolmente maggiore rispetto al valore predefinito di un carattere. Almeno 8192 dovrebbe essere sufficiente per supportare la dimensione del buffer predefinita di XMLEntityManager all'interno della copia di Apache2 di JAXP.

Questa non è la soluzione più pulita, in quanto comporta la sottoclasse delle classi JAXP interne, ma funziona.

Altri suggerimenti

Molto probabilmente, non si desidera avere più di un documento nello stesso flusso contemporaneamente. Non penso che SAXReader sia abbastanza intelligente da fermarsi quando arriva alla fine del primo documento. Perché è necessario avere più documenti nello stesso flusso come questo?

Penso che dovresti aggiungere un adattatore, qualcosa per avvolgere il flusso e fare in modo che quella cosa restituisca la fine del file quando vede l'inizio del prossimo documento. Per quanto ne so, i parser scritti, andranno fino alla fine del file o un errore ... e vedere un altro <?xml version="1.0"?> sarebbe sicuramente un errore.

Supponendo che tu sia responsabile del posizionamento dei documenti nello stream, in primo luogo dovrebbe essere facile delimitare i documenti in qualche modo. Ad esempio:

// 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);
}

Quindi, quando si legge dallo stream, leggere in un array fino a quando non viene rilevato 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();
}

Poiché 4 è un valore di carattere non valido che non incontrerai tranne dove lo aggiungi esplicitamente. Permettendo così di dividere i documenti. Ora basta avvolgere l'array di caratteri resuling per l'input in SAX e il tuo ben fatto.

...
  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);
  }
...

Nota che il ciclo termina quando ottiene un documento di lunghezza 0. Ciò significa che dovresti aggiungere un secondo DOC_TERMINATOR dopo l'ultimo documento che devi aggiungere qualcosa per rilevare la fine del flusso in getNextDocument ().

L'ho già fatto avvolgendo il lettore di base con un altro lettore di mia creazione che avesse una capacità di analisi molto semplice. Supponendo di conoscere il tag di chiusura per il documento, il wrapper analizza semplicemente una corrispondenza, ad es. per " < / MyDocument > " ;. Quando rileva che restituisce EOF. Il wrapper può essere adattato analizzando il primo tag di apertura e restituendo EOF sul tag di chiusura corrispondente. Ho scoperto che non era necessario rilevare effettivamente il livello per il tag di chiusura poiché nessun documento in cui avevo usato il tag documento all'interno di se stesso, quindi è stato garantito che la prima occorrenza del tag di chiusura ha terminato il documento.

Come ricordo, uno dei trucchi era di chiudere il blocco wrapper (), poiché il lettore DOM chiude la sorgente di input.

Quindi, dato l'input di Reader, il tuo codice potrebbe apparire come:

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

Il metodo eof () restituisce true se viene rilevato EOF. Il metodo next () segnala al lettore di interrompere la restituzione di -1 per read ().

Speriamo che questo ti indichi in una direzione utile.

- Kiwi.

Vorrei leggere il flusso di input in un buffer interno. A seconda della dimensione del flusso totale prevista, leggerei l'intero flusso e quindi analizzerei o rileverei il confine tra un xml e il successivo (cercare

L'unica vera differenza tra la gestione di uno stream con un xml e uno stream con più xmls è il buffer e la logica divisa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top