Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

CHUNKEDXML, LIRE DU XML PAR MORCEAU


Information sur la source

Catégorie :Astuces Classé sous : XML, parser, chunk Niveau : Initié Date de création : 27/06/2008 Date de mise à jour : 27/06/2008 17:39:04 Vu : 2 552

Note :
Aucune note

Commentaire sur cette source (15)
Ajouter un commentaire et/ou une note


Description

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

  • 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;
  • }
  • }
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>
 

Historique

27 juin 2008 17:39:04 :
Beh koi ? Faut-il tout le temps se justifier ('tain de monde numérique)

Commentaires et avis

signaler à un administrateur
Commentaire de AlexN le 27/06/2008 18:06:40

Quizz :

Parmi toutes les erreurs de conception de cette classe quelle est celle qui vous semble la pire ?

signaler à un administrateur
Commentaire de EagleUnderscoreOne le 30/06/2008 13:30:28

Je sais pas, je connais rien en XML, mais rapidement, ça ça m'a l'air étrange :

#  public boolean isAvailable() {
# try {
# return (this.stream.available() > 0);
# } catch(IOException e) {
# return false;
# }
# }
Si tu récupères une exception, c'est pas forcément parce qu'il n'y a plus de caractères dispos. Si?...

signaler à un administrateur
Commentaire de sheorogath le 01/07/2008 11:07:37 administrateur CS

non mais ca veux de toute facon dire qu'il ne peux plus lire ...
le while (true) pique les yeux

les close qu'il faudrais dans des finally

apres quant a l'utilite de la source je m'avance pas

signaler à un administrateur
Commentaire de sheorogath le 01/07/2008 11:09:43 administrateur CS

#  while (true) {
# int byteRead = this.stream.read();
# if (byteRead < 0) break;

=>
int byteRead ;
#  while ((byteRead = this.stream.read()) > 0)

signaler à un administrateur
Commentaire de EagleUnderscoreOne le 01/07/2008 11:27:51

Ouais enfin la description de la méthode c'est :
@return vrai ou faux selon qu'il reste des caractères dans le flux xml

Pour le while(true), ta solution revient au même finalement, c'est juste condensé en une ligne mais ce sont les mêmes opérations qui sont effectuées, une lecture, une assignation, un test. A la déclaration de variable à chaque pas de la boucle près, certes :p. Je vois pas, y'a un autre souci?

signaler à un administrateur
Commentaire de sheorogath le 01/07/2008 20:41:47 administrateur CS

ouaip des break qui servent a rien et qui a mon sens polluent le while de plus la je n'utilise qu'un int
dans la version original un int etait realloue a chaque fois ... "oui mais il y a le GC"
ce n'est pas une raison pour moi

il a demande les fautes de conception pour moi elles sont la

signaler à un administrateur
Commentaire de AlexN le 01/07/2008 20:50:10

Ce qu'il manque principalement ce sont :
- la non prise en compte de l'IOException masque des erreurs
- la libération des ressources occupées par les propriétés des instances (la fermeture du fichier, la libération de la mémoire).

Sinon la différence entre un while (true) et un while (condition) c'est surtout une question d'habitude. Si le code contenu dans la boucle ne vérifie pas la ou les conditions de sortie, quelque soit la syntaxe, c'est une boucle sans fin.

L'intérêt principale de cette classe est qu'elle permet la lecture de fichiers xml volumineux, dans un minimum d'espace mémoire. Cette classe peut être utilisée avec un analyseur (parser), qui va demander les blocs xml au fur et à mesure pour transformer leur contenu en données ou en objets (désérialiser). Cette classe permet aussi de ne pas utiliser des librairies plus lourdes (SAX, JDOM, ...) lorsque ce n'est pas utile. Utiliser un tank pour ouvrir une porte en bois ce n'est pas l'idéal.

signaler à un administrateur
Commentaire de AlexN le 01/07/2008 21:12:16

Les break, ils vont avec le while (true) ;o), c'est juste que tu n'es pas trop habitué à ce style d'écriture.
Un int alloué ? Tu voulais peut être dire un Integer.
L'allocation d'un int se fera plutôt dans la pile (gérée par la JVM) tandis qu'un objet aura plus de chance de se retrouver dans le tas (géré par le GC). Gagner un int ne fait pas gagner grand chose, à mon avis. Mais par contre, je ne vois pas où tu le gagnes.

signaler à un administrateur
Commentaire de sheorogath le 01/07/2008 21:14:47 administrateur CS

#  while (true) {
# int byteRead = this.stream.read();
tu le recre a chaque fois
apres certe c'est pas grand chose mais bon on prends vite l'habitude de toujours faire ca ...

sinon pour les break j'en demors pas :p certes ce n'est qu'un point de vu personnel mais je des que je peux faire sans je fais sans

signaler à un administrateur
Commentaire de AlexN le 02/07/2008 07:53:01

La c'est du chipotage.
Je fais un minimum confiance au compilateur. Et dans la mesure où il voit que cette variable, de type primitif, est utilisée dans la boucle, il me fera le privilège (Oh ! grand privilège ! Donnes moi une case mémoire siouplai...) de réserver l'espace nécessaire, sans l'allouer à chaque passage. Sinon ce n'est pas un bon compilateur (et toc !).

signaler à un administrateur
Commentaire de EagleUnderscoreOne le 02/07/2008 10:57:39

C'est sûr que là on est en plein chipotage quand on voit la puissance des machines actuelles ça m'étonnerais qu'il y ait une différence mesurable entre les deux solutions...

signaler à un administrateur
Commentaire de sheorogath le 02/07/2008 12:03:00 administrateur CS

ouais je suis d'accord mais la c'est 4o
ya des classes ca pourrais etre BEAUCOUP plus

et 4o par ci, 4o par la ... ca monte vite
les optimisations que l'ont peu faire des le debut autant les faire ...
je ne sais pas comment se debrouille le GC dans ce cas mais il y a des chance que se soit plus rapide...

signaler à un administrateur
Commentaire de boumarsel le 04/07/2008 13:25:19

Bonjour,

Je ne sais pas si ça a été dit ou non mais une chose que j'apprécie c'est le respect du standard pour la doc/commentaires dans le code.

Cordialement

signaler à un administrateur
Commentaire de AlexN le 04/07/2008 20:08:07

Merci ;o)

signaler à un administrateur
Commentaire de Twinuts le 30/07/2008 19:47:41 administrateur CS

Salut,

le code est de bonne qualité (+1 pour le commentaire de boumarsel), mais comme je maudit le XML je ne testerai pas...

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

Parser un fichier XML avec un schéma donné [ par biniouman ] Bonjour,Je cherche à parser avec jaxp et sax un fichier xml en fonction d'un schéma. Mon fichier est tout bete, mais je ne sais pas par où commencer S parseur XML [ par haineT ] Salut a tous,1er prog en java, 1er probleme et donc premier post :). Je suis developpeur flash, et je voudrais elargir mes connaissances sur java. J'a Mise e forme d'un fichier des info retirer a l'aide d'un Parser xml [ par Caralho27 ] J'effectuen un parsing sur plusieurs fichiers à l'aide d'un programme JAVA mais il me faudrait mettre les valeurs que g rechercher dans un tableau.Je Parser du xml [ par Mikonyx ] Salut, J'ai besoin de parser du xml mais, ne voulant pas obliger l'utilisateur à installer un parser xerces ou aelfred, je souhaite le faire uniquemen SWT [ par julio13 ] Salut à tous,voila ma question :je suis en train de développer un plugin eclipse. Au départ, le plugin est initialisé avec un fichier .xml pour la con parser xml [ par fredmorvant29 ] bonjour je veu parser un flux xml pour en retirer les info sous forme de liste.ex: &lt;prod&gt; &lt;nom&gt;toto&lt;/nom&gt; &lt;prix&gt;10&lt;/p le XML parser SAX : pb lorsque <balise></balise> [ par Enguerrant ] hello!voilà, j'utilise SAX pour parser un fichier XML, et çà se passe plutot bien !! sauf lorsque dans le fichier en question, il n'y a, à un endroit parser du XML en sauver dans une liste [ par damien781 ] Hello, J'ai un fichier XML sous cette forme (pour implémenter une logique plus simple, cette forme peut etre modifier)//dans un tableau/liste ce qui s Parser XML [ par mlelorra ] SalutJe voudrais pouvoir parser une grande chaine de caractere avec Dom ou SAX mais il n'accepte que des File, ImputStream, URI...comment faire ?merci parser xml | Handler | question [ par Yanzisf ] Bonjour, voici un bout d'un xml que je parse avec SAX : [code] &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;Tache executable='true'&gt; &nbsp;&nbsp;&nb


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 2,761 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.