Question

J'essaie de lire un seul document XML d'un flux à la fois à l'aide de dom4j, de le traiter, puis de passer au document suivant du flux. Malheureusement, SAXReader de Dom4j (utilisant JAXP sous la couverture) continue de lire et d'étrangler l'élément de document suivant.

Existe-t-il un moyen de faire en sorte que SAXReader cesse de lire le flux une fois qu'il a trouvé la fin de l'élément de document? Y a-t-il une meilleure façon d’y parvenir?

Était-ce utile?

La solution

J'ai pu faire fonctionner cette gymnastique avec des cours internes JAXP:

  • Créer un scanner personnalisé, une sous-classe de XMLNSDocumentScannerImpl
    • Créez un pilote personnalisé, une implémentation de XMLNSDocumentScannerImpl.Driver, dans l'analyseur personnalisé qui renvoie END_DOCUMENT lorsqu'il voit une déclaration ou un élément. Obtenez le ScannedEntity à partir de fElementScanner.getCurrentEntity (). Si l'entité dispose d'un PushbackReader, repoussez les caractères non lus restants dans la mémoire tampon de l'entité sur le lecteur.
    • Dans le constructeur, remplace le fTrailingMiscDriver par une instance de ce pilote personnalisé.
  • Créez une classe de configuration personnalisée, une sous-classe de XIncludeAwareParserConfiguration, qui remplace l'action DOCUMENT_SCANNER par une instance de ce scanner personnalisé dans son constructeur.
  • Installez une instance de cette classe de configuration personnalisée en tant que & "; com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration &"; il sera donc instancié lorsque la classe SAXReader de dom4j tentera de créer un lecteur JAXP XMLReader.
  • Lorsque vous transmettez un lecteur à la méthode SAXReader.read () de dom4j, fournissez un PushbackReader avec une taille de tampon considérablement plus grande que la valeur par défaut à un caractère. Au moins 8192 devraient suffire à prendre en charge la taille de tampon par défaut de XMLEntityManager dans la copie JAXP d’Apache2.

Ce n'est pas la solution la plus propre car elle implique de sous-classer les classes internes JAXP, mais cela fonctionne.

Autres conseils

Très probablement, vous ne voulez pas avoir plus d'un document dans le même flux en même temps. Je ne pense pas que SAXReader soit assez intelligent pour s'arrêter lorsqu'il arrive à la fin du premier document. Pourquoi est-il nécessaire d'avoir plusieurs documents dans le même flux comme celui-ci?

Je pense que vous devez ajouter un adaptateur, un élément pour envelopper le flux et le renvoyer en fin de fichier lorsqu'il voit le début du document suivant. Autant que je sache, les analyseurs tels qu'ils sont écrits iront jusqu'à la fin du fichier ou une erreur ... et voir un autre <?xml version="1.0"?> serait certainement une erreur.

En supposant que vous soyez responsable de placer des documents dans le flux en premier lieu, il devrait être facile de délimiter les documents d’une manière ou d’une autre. Par exemple:

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

Ensuite, lors de la lecture du flux, lu dans un tableau jusqu'à ce que DOC_TERMINATOR soit rencontré.

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

Puisque 4 est un caractère invalide, vous ne le rencontrerez pas, sauf si vous l'ajoutez explicitement. Vous permettant ainsi de scinder les documents. Enroulez maintenant le tableau de caractères de résiliation pour l’entrée dans SAX et votre bon à emporter.

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

Notez que la boucle se termine quand elle obtient un document de longueur 0. Cela signifie que vous devez soit ajouter un second DOC_TERMINATOR après que le dernier document de vous ait besoin d'ajouter quelque chose pour détecter la fin du flux dans getNextDocument ().

Je l'ai déjà fait auparavant en enveloppant le lecteur de base avec un autre lecteur de ma propre création, doté d'une capacité d'analyse très simple. En supposant que vous connaissiez la balise de fermeture du document, le wrapper analyse simplement une correspondance, par exemple. pour " < / MyDocument > " ;. Quand il détecte qu'il retourne EOF. Le wrapper peut être rendu adaptatif en analysant la première balise d'ouverture et en retournant EOF sur la balise de fermeture correspondante. J’ai trouvé qu’il n’était pas nécessaire de détecter réellement le niveau de la balise de fermeture car aucun document n’avait utilisé la balise de document en elle-même; il était donc garanti que la première occurrence de la balise de fermeture mettait fin au document.

Si je me souviens bien, une des astuces consistait à fermer le bloc wrapper (), car le lecteur DOM ferme la source d'entrée.

Ainsi, avec l'entrée de Reader, votre code pourrait ressembler à:

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

La méthode eof () renvoie true si EOF est rencontré. La méthode next () indique au lecteur de ne plus renvoyer -1 pour read ().

Espérons que cela vous indique une direction utile.

- Kiwi.

Je lirais le flux d’entrée dans un tampon interne. En fonction de la taille totale attendue du flux, je lirais le flux entier et l’analyserais ou détecterais la limite entre un xml et le suivant (recherchez

La seule différence réelle entre le traitement d'un flux avec un xml et un flux avec plusieurs xml est la mémoire tampon et la logique de division.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top