Main Contents

[Objective-c] Application iPhone qui parse un fichier XML

juillet 28, 2011

Dans cet article nous allons voir comment parser un fichier Xml et récupérer les informations contenues. Dans un second article, on affichera ces infos dans un tableView. Merci à Medhi.N pour l’idée de ce tutorial! Allez C’est parti…

I) Introduction

Nous allons donc créer une application permettant de parser un fichier xml ce trouvant sur le net. Par exemple un fichier contenant des contacts.

1
2
3
4
5
6
7
8
9
<contacts>
   <Contact>
      <nom>Smith</nom>
      <prenom>Joe</prenom>
      <tel>321-123-4567</tel>
      <email>joe@smith.com</email>
   </Contact>
<!--...-->
</contacts>

Vous pouvez retrouver un exemple de fichier à cette adresse : http://www.jkraft.fr/testXml.xml

II) Contact

Commencons par créer un projet de type « Navigation-Based ». Notre fichier XML contenant des contacts nous allons créer une classe qui va gérer ces données. Ajouter une NSObject subclass, nommer la Contact.m et copiez ce code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Contact.h
#import <Foundation/Foundation.h>
 
@interface Contact : NSObject {
	NSString *prenom;
	NSString *nom;
	NSString *tel;
	NSString *email;
}
 
@property (nonatomic, retain) NSString *prenom;
@property (nonatomic, retain) NSString *nom;
@property (nonatomic, retain) NSString *tel;
@property (nonatomic, retain) NSString *email;
 
@end
1
2
3
4
5
6
7
8
9
10
11
//Contact.m
#import "Contact.h"
 
@implementation Contact
 
@synthesize prenom;
@synthesize nom;
@synthesize tel;
@synthesize email;
 
@end

Il n’y a rien de compliqué ici, on a crée une classe contact contenant 4 champs.

III) Parseur XML

Maintenant on va s’attaquer au parseur XML. C’est cette classe qui va ouvrir et fermer les balises et récupérer les données. Ajouter une 2eme classe de type NSObject du nom de XMLToObjectParser (par : Andrew).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//XMLToObjectParser.h
#import <Foundation/Foundation.h>
 
@interface XMLToObjectParser : NSObject <NSXMLParserDelegate> {
	NSString *className;
	NSMutableArray *items;
	NSObject *item; // stands for any class    
	NSString *currentNodeName;
	NSMutableString *currentNodeContent;
}
- (NSArray *)items;
- (id)parseXMLAtURL:(NSURL *)url
		   toObject:(NSString *)aClassName
		 parseError:(NSError **)error;
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//XMLToObjectParser.m
#import "XMLToObjectParser.h"
 
@implementation XMLToObjectParser
 
- (NSArray *)items
{
	return items;
}
 
- (id)parseXMLAtURL:(NSURL *)url
		   toObject:(NSString *)aClassName
		 parseError:(NSError **)error
{
	[items release];
	items = [[NSMutableArray alloc] init];
	className = aClassName;
	NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
	[parser setDelegate:self];
	[parser parse];
 
	if([parser parserError] && error) {
		*error = [parser parserError];
	}
	[parser release];
	return self;
}
 
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qName
	attributes:(NSDictionary *)attributeDict
{
	NSLog(@"Open tag: %@", elementName);
	if([elementName isEqualToString:className]) {
		// create an instance of a class on run-time  
		item = [[NSClassFromString(className) alloc] init];
	}
	else {
		currentNodeName = [elementName copy];
		currentNodeContent = [[NSMutableString alloc] init];
	}
}
 
- (void)parser:(NSXMLParser *)parser
 didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qName
{
	NSLog(@"Close tag: %@", elementName);
	if([elementName isEqualToString:className]) {
		[items addObject:item];
		[item release];
		item = nil;
	}
	else if([elementName isEqualToString:currentNodeName]) {
		// use key-value coding     
		[item setValue:currentNodeContent forKey:elementName];
 
		[currentNodeContent release];
		currentNodeContent = nil;
 
		[currentNodeName release];
		currentNodeName = nil;
	}
}
 
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string
{
	[currentNodeContent appendString:string];
}
 
- (void)dealloc
{
	[items release];
	[super dealloc];
}
 
@end

Cette classe va parcourir le xml et s’il trouve une balise portant le nom de la classe Contact, y copier les informations et retourner un objet de ce type.

IV) Code principal

Il ne reste plus qu’a faire fonctionner tout ca ensemble. Surtout n’oubliez les imports des 2 fichiers, XMLToObjectParser.h et Contact.h. Dans la classe principale de votre application,normalement RootViewController.m , il suffit d’ajouter ceci (dans mon cas dans le viewDidLoad).

1
2
3
4
5
6
7
8
9
10
11
12
- (void)viewDidLoad {
	NSURL *url = [NSURL URLWithString: @"http://www.jkraft.fr/testXml.xml"];
	XMLToObjectParser *myParser = [[XMLToObjectParser alloc]
parseXMLAtURL:url toObject:@"Contact" parseError:nil];
 
	for(int i = 0; i < [[myParser items] count]; i++) {
		NSLog(@"Prénom: %@", [(Contact *)[[myParser items] objectAtIndex:i] prenom]);
	}
 
 
    [super viewDidLoad];
}

Une petite compilation et lors de l’exécution vous verrez apparaître dans la console toutes les ouvertures et fermetures des balises XML lors du parsage du fichier. Puis à la fin les prénoms lors du parcours de l’objet retourné.

Vous pouvez retrouver les fichiers sources ici : https://github.com/Jekar/jkraft-tuto

Catégorie(s): Développement, Iphone, Objective-C, Tutorial | Comments (25)

25 Comments

  1. Medhi Naitmazi (1 comments) avril 25, 2009 @ 14 h 42 min

    Super nice le tutoriel iPhone pour lire un xml, merci !

    plus qu’à jouer avec les vues 🙂

  2. [Objective-c] Application iPhone, XML et UITableView | MacFan avril 26, 2009 @ 9 h 57 min

    […] Billets similaires [Objective-c] Application iPhone qui parse un fichier XML […]

  3. Petite liste de tutorial Objective-C et iPhone | MacFan mai 11, 2009 @ 23 h 42 min

    […] [Objective-c] Application iPhone qui parse un fichier XML […]

  4. Mercutio (1 comments) juin 7, 2009 @ 18 h 42 min

    Bonjour et merci pour ton tutoriel « Application iPhone, XML et UITableView ».

    Ca fait plaisir de trouver des explications bien faîtes en français (non pas que l’anglais soit rédhibitoire mais j’apprends moins vite en shakespearien!).

    Je n’ai malheureusement pas terminé ton tutoriel car une erreur apparait dans la dernière méthode à mettre en place (j’ai rajouté l’erreur là où elle apparaît dans le code) :
    – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here — for example, create and push another view controller.
    //NSMutableArray *tableau;
    ContactViewController *contactViewController = [[ContactViewController alloc] initWithNibName:@ »ContactView » bundle:nil];
    contactViewController.MonContact=[tableau objectAtIndex:indexPath.row];
    ///error : ‘tableau’ undeclared (first use in this function)///
    self.navigationItem.title=[[tableau objectAtIndex:indexPath.row] nom];
    [self.navigationController pushViewController:contactViewController animated:YES];
    [contactViewController release];
    }

    Je pense qu’il faut récupérer le ‘tableau’ de la classe RootViewController instanciée. Sais-tu comment faire ?

    Cordialement,
    Marc.

  5. NoNo (1 comments) décembre 4, 2009 @ 14 h 48 min

    Euh… juste un détail, le parse d’un XML peut durer un peu de temps parfois sur un Mac donc sur iPhone c’est pire, c’est pour cela qu’il est conseillé de lancer le Parse dans un nouveau thread !

  6. dhia (1 comments) février 7, 2010 @ 1 h 51 min

    J’ADORE VOS TUTOS.
    MERCI BCP
    BONNE CONTINUATION

  7. Sandy (2 comments) mars 19, 2010 @ 12 h 48 min

    slt et merci pour ce tutoriel ca aide beaucoup moi particulierement

    je viens d essayer ton tuto sur xml ca marche sans faute et sur la console je vois comme le Xml est ouvert et parcouru mais je ne vois pas les prenoms s afficher;
    et je n ai pas la fonction (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath dans mon programme
    que faire merci d avance
    sandy

  8. Hugo (1 comments) avril 22, 2010 @ 10 h 56 min

    Merci pour ce TUTO,

    J’ai tenté de faire la meme chose avec mon fichier XML (utf-8), malheureusement ce dernier contient des accents dans les prénoms « Jérome » …
    Y a t-il quelque chose à rajouter dans le code pour que cela fonctionne ?

  9. Alexandre (1 comments) juin 20, 2010 @ 2 h 12 min

    Salut, super tuto il m’a été d’un grand aide, par contre j’ai aussi un problème avec mon fichier xml qui a des accents, lorsqu’il traite une ligne avec un accent le programme arrête de lire dans le fichier, comment on peut régler ce problème?

    merci
    alex

  10. Hamza AOUNI (1 comments) avril 5, 2011 @ 15 h 21 min

    Salut,merci pour votre aide précieuse. Très bon tuto.
    Bonne Continuation.

  11. ettibo (1 comments) juillet 28, 2011 @ 12 h 13 min

    Bonjour, j’ai suivi le tuto, et je me suis aperçu que pour moi il ne marchait pas.
    J’ai du d’abord faire une modification et faire hériter la classe XMLToObjectParser par NSXMLParserDelegate pour que le setDelegate ne me fasse pas un warning. Et même en faisant celà, les deux méthodes de parsing didStartElement et didEndElement ne sont pas appelées, cela doit doit être une soucis avec le delegate. Si quelqu’un a une idée.
    Merci d’avance

  12. kraft (36 comments) juillet 28, 2011 @ 18 h 45 min

    Bien vu!
    Il suffit de rajouter à la classe.
    j’ai modifié le tuto et ça fonctionne très bien.

  13. ben (1 comments) septembre 16, 2011 @ 15 h 30 min

    Bonjour,

    Déjà merci pour votre tuto, il est super clair, mais j’ai un soucis. En fait lors de la simulation ça me met ce message d’erreur :

    *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[NSMutableArray insertObject:atIndex:]: attempt to insert nil object at 0’

    ça s’arrete ligne 53 de XMLToObjectParser.m :

    [items addObject:item];

    ça lit bien le xml, c’est au moment de mettre dans ‘items’ que ça coince.

    Pourriez vous me donner un coup de main? car je nage dans la choucroute…

    Merci d’avance

  14. Philippe (1 comments) septembre 30, 2011 @ 22 h 41 min

    Bonjour

    Merci de votre tutoriel. Cependant, je rencontre la même erreur que le message précédent…

    Pourriez-vous nous aider ?

    Merci

  15. Romain (6 comments) octobre 17, 2011 @ 14 h 57 min

    Bonjour a tous !

    Alors moi je rencontre un probleme, et ce peut importe la façon dont je m’ y prend.

    Tout d’abord juste pr infos je tourne sous Lion et Xcode 4.2 …

    Alors mon probleme se pose dans le fichier XMLToObjectParser.m

    Chaque ligne où se trouve un [items release]; j’ ai 2 erreurs associées :

    « ARC forbids explicit message send of ‘release' » et « ‘release is unavailable: not avalaible in automatic reference counting mode

    Et du coupf forcement je ne peux rien faire d’ autre !

    Merci de votre aide pr le decodage de ce probleme !

  16. kraft (36 comments) octobre 17, 2011 @ 15 h 02 min

    Oui ça on va avoir des soucis avec les anciens projet et xcode 4.2 et ARC (Automatic Reference Counting).
    Du coup, soit il faut nettoyer les classes, soit désactiver ARC

  17. Romain (6 comments) octobre 17, 2011 @ 18 h 52 min

    Ah … on peut le desactiver ? Je vois pas trop comment !!

    D’autant que je suis vraiment un « grand debutant » … 🙂

  18. kraft (36 comments) octobre 17, 2011 @ 19 h 18 min

    Oui, regarde la : http://www.jkraft.fr/2011/10/17/objective-c-xcode-4-2-comment-desactiver-arc/

  19. Romain (6 comments) octobre 18, 2011 @ 7 h 25 min

    Bien le merci !

    Effectivement tout roule ! Les erreurs de type ARC continuent a etre affichées mais on peut tout de meme Build.

    Encore merci

  20. Quelques bons trucs pour XCode » Thiébault Michaël février 21, 2012 @ 15 h 14 min

    […] parser un fichier xml à distance: cliquez ici […]

  21. jd (1 comments) février 22, 2012 @ 10 h 08 min

    Bonjour.
    Il y a une difference entre le code sur github et celui dans le tutoriel.

    Par ex :
    NSLog(@ »Close tag: %@ », elementName);
    if([elementName isEqualToString:className]) {
    [items addObject:item];
    //[item release]; (commenté dans github et pas dasn le tuto)

    item = nil;
    }
    else if([elementName isEqualToString:currentNodeName]) {
    // use key-value coding
    [item setValue:currentNodeContent forKey:elementName];

    //[currentNodeContent release];
    currentNodeContent = nil;

    //[currentNodeName release];
    currentNodeName = nil;
    }

    Lequel doit ton utiliser?
    MErci d’avance, et super tuto/

  22. kraft (36 comments) février 24, 2012 @ 10 h 30 min

    Le code sur gitHub est le bon !

  23. xoledas (1 comments) juillet 9, 2012 @ 20 h 05 min

    bonjour, j’ai comme vous, le meme code cepandant les prénoms ne s’affiche pas…
    il y a t-il quelque chose à faire en plus ??

  24. Rock (1 comments) avril 2, 2015 @ 10 h 43 min

    Bonjour,

    Ce tutoriel est-il compatible pour parser un fichier HTML ?

  25. admin (1 comments) juillet 23, 2015 @ 13 h 55 min

    Surement, la méthode reste la même. Les fonctions seront différentes par contre.

Leave a comment