Chunkedxml, lire du xml par morceau

Contenu du snippet

Cette classe à pour vocation de fournir une fonctionnalité légère pour la lecture de gros flux XML
Certains flux xml (notamment ceux utilisés par mes script de collecte de données) peuvent être très volumineux (20 à 100 Mo, quand c'est pas du Go).
Leur lecture par des procédés classiques :
- SAX and Co
- analyse par expression rationnelle de la globalité du flux présent en mémoire
fait souvent déborder la mémoire (Heap Space).

L'idée est donc de fournir un service de lecture de ces flux par morceau (chunk), afin de limiter la consommation de mémoire et surtout de permettre la lecture complète du flux sans faire tomber le script en dépassement de capacité mémoire (Heap Overflow)

Source / Exemple :


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**

  • Cette classe à pour vocation de fournir une fonctionnalité légère pour la lecture de gros flux XML.
  • Certains flux xml (notamment ceux utilisés par mes script de collecte de données)
  • peuvent être très volumineux (20 à 100 Mo, quand c'est pas du Go).
  • <br/>
  • Leur lecture par des procédés classiques :
  • <ul>
  • <li>SAX and Co</li>
  • <li>analyse par expression rationnelle de la globalité du flux présent en mémoire</li>
  • </ul>
  • fait souvent déborder la mémoire (Heap Space).
  • <br/>
  • L'idée est donc de fournir un service de lecture de ces flux par morceau (chunk),
  • afin de limiter la consommation de mémoire et surtout de permettre la lecture complète du flux
  • sans faire tomber le script en dépassement de capacité mémoire (Heap Overflow)
  • <br/>
  • 1) Principe de fonctionnement de cette classe :
  • <br/><br/>
  • On instancie un objet de la classe en fournissant :
  • <br/>
  • <ul>
  • <li>le nom du fichier XML à morceler</li>
  • <li>le texte de la balise qui va délimiter les morceaux</li>
  • </ul>
  • L'instanciation, vérifie l'existence du flux et prépare sa lecture.
  • <br/><br/>
  • 2) On appelle la méthode getNextChunk() chaque fois qu'on veut un morceau du flux.
  • La méthode répondra par une chaîne correspondant au morceau suivant ou null lorsqu'elle est arrivée en fin de flux.
  • <br/><br/>
  • Dans un premier temps, il s'agit de permettre la lecture de fichiers locaux téléchargés manuellement.
  • Mais l'idéal serait de permettre le téléchargement du flux depuis son url d'origine, sa décompression et sa lecture.
  • <br/><br/>
  • @author AlexN
*
  • /
public class ChunkedXML { // Propriétés de construction private String filename; // nom du fichier (ou url (TODO)) private String delimiter; // texte non xml de la balise délimitant les morceaux // Propriétés internes private Boolean isValid; // indicateur si le nom de fichier ou l'url fournie correspond à une ressource disponible private File file; // objet fichier pour la lecture par morceaux private FileInputStream stream; // flux de lecture /**
  • Point d'entrée du programme de test
  • @param args
  • /
public static void main(String[] args) { ChunkedXML myXML = new ChunkedXML("my.xml", "chunck"); String chunk; while ((chunk = myXML.getNextChunk()) != null) { System.out.println(chunk.length() + " " + chunk); chunk = null; // Déréférencement des données pour aider le Garbage Collector } } /**
  • Constructeur de ChunkedXML
  • @param filename : nom du fichier
  • @param delimiter : texte brut de la balise de délimitation "balise" et non "</balise>".
  • Le delimiteur n'a pas besoin d'être capitalisé (compareIgnoreCase() pour la recherche du délimiteur)
  • /
public ChunkedXML(String filename, String delimiter) { this.filename = filename; this.delimiter = delimiter; this.isValid = isValid(); // vérification de la validité du flux et préparation de sa lecture } /**
  • Verifie que les paramètres du constructeur sont valides
  • @return : vrai ou faux selon le résultat des tests
  • /
private Boolean isValid() { if (this.filename == null) { System.err.println("La référence au fichier est nulle : WTF ???"); return false; } this.file = new File(this.filename); // Le fichier existe-t-il et est-il lisible ? if(!this.file.canRead()) { System.err.println("Je ne peux pas lire le fichier (introuvable ou illisible) : " + this.filename); return false; } // Le fichier contient-il quelquechose ? if(this.file.length() == 0L) { System.err.println("Le fichier est vide : " + this.filename); return false; } // Préparation du flux de lecture try { this.stream = new FileInputStream(this.file); } catch(FileNotFoundException e){ System.err.println("Je ne peux pas convertir le fichier en flux de lecture : " + this.filename); return false; } // Tout semble ok return true; } /**
  • Méthode pour savoir s'il reste des caractères disponibles dans le flux xml
  • @return vrai ou faux selon qu'il reste des caractères dans le flux xml
  • /
public boolean isAvailable() { try { return (this.stream.available() > 0); } catch(IOException e) { return false; } } /**
  • Lit un morceau du flux xml et s'arrête au délimiteur
  • @return le morceau de flux xml qui a été lu.
  • /
public String getNextChunk() { try { if (!this.isValid || this.stream.available() <= 0) return null; StringBuffer tag = new StringBuffer(); ByteArrayOutputStream baos = new ByteArrayOutputStream((int)this.file.length()); boolean inTag = false; while (true) { int byteRead = this.stream.read(); if (byteRead < 0) break; if (byteRead == '<') { inTag = true; tag = new StringBuffer(); tag.append((char)byteRead); } else if (inTag) { if (byteRead != ' ') tag.append((char)byteRead); // Ignorer les espaces if (byteRead == '>') { inTag = false; StringBuffer xmlTag = new StringBuffer("</"); xmlTag.append(this.delimiter); xmlTag.append(">"); if (tag.toString().equalsIgnoreCase(xmlTag.toString())) { baos.write(byteRead); break; } } } baos.write(byteRead); } baos.flush(); String chunk = new String(baos.toByteArray()); baos.close(); baos = null; // Déréférencement des données pour aider le Garbage Collector return chunk; } catch(IOException e) { System.err.println("Problème IO lors de la lecture du flux"); } return null; } }

Conclusion :


Fichier d'exemple (my.xml) :

<root>

<chunck>
<data1>this is the data 1 for chunk 1</data1>
<data2>this is the data 2 for chunk 1</data2>
<data3>this is the data 3 for chunk 1</data3>
</chunck>
<chunck>
<data1>this is the data 1 for chunk 2</data1>
<data2>this is the data 2 for chunk 2</data2>
<data3>this is the data 3 for chunk 2</data3>
</chunck>
<chunck>
<data1>this is the data 1 for chunk 3</data1>
<data2>this is the data 2 for chunk 3</data2>
<data3>this is the data 3 for chunk 3</data3>
</chunck>
<chunck>
<data1>this is the data 1 for chunk 4</data1>
<data2>this is the data 2 for chunk 4</data2>
<data3>this is the data 3 for chunk 4</data3>
</chunck>

</root>

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.