Bonjour à tous !
Aujourd'hui, nous allons parler de XML, et de parsage.
Nous allons lire un fichier xml (un exemple très simple) puis nous l'afficherons.

1) Qu'est-ce que XML ?
Si vous ne savez pas ce qu'est XML, je vous invite à faire deux trois recherches sous Google
- Wikipedia
- Communauté française autour du XML
- Cours sur altruiste.com
- Tutoriel sur developpez.com
En gros, ce qu'il faut retenir : XML pour eXtensible Markup Language est un langage utilisant des balises, et ce de manière simplifiée. Vous pouvez en effet définir vos propres balises.
Voici à quoi peut ressembler un fichier XML :
<Book id="1">
<title>Circumference</title>
<author>Nicholas Nicastro</author>
<summary>Eratosthenes and the Ancient Quest to Measure the Globe.</summary>
</Book>2) Et sur iPhone alors ?
1. Quel fichier xml
Nous allons partir de ceci :
<?xml version="1.0" encoding="UTF-8"?>
<Monde>
<pays>
<nom>France</nom>
<capitale>Paris</capitale>
<habitants>60M</habitants>
</pays>
<pays>
<nom>Suisse</nom>
<capitale>Berne</capitale>
<habitants>7,5M</habitants>
</pays>
</Monde>Copier/coller ce code dans un fichier txt, puis changer l'extension en .xml
2. Nouveau projet
Créons tout d'abord un projet "Navigation Based Application".
Ajoutez un fichier "NSObject Subclass" que nous nommerons pays.m
NB : pays correspond à la balise <pays>
Ajoutez le code suivant :
#import <Foundation/Foundation.h>
@interface pays : NSObject {
// Les NSStrings correspondent aux balises dans pays. Attention, mettre le même nom que vos balises
NSString *nom;
NSString *capitale;
NSString *habitants;
}
@property (nonatomic, retain) NSString *nom;
@property (nonatomic, retain) NSString *capitale;
@property (nonatomic, retain) NSString *habitants;
@endN'oubliez pas les @synthesize dans le .m 
3. Attaquons-nous au Parser
(D'après le blog d'Atrexis)
Créez une nouvelle subclass NSObject "XMLToObjectParser". Cette classe va permettre d'ouvrir et fermer les balises pour récupérer les données.
#import <Foundation/Foundation.h>
@interface XMLToObjectParser : NSObject {
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;
@endDans le .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];
}
@endJ'ai gardé la méthode - (id)parseXMLAtURL:(NSURL *)url
toObject:(NSString *)aClassName
parseError:(NSError **)error
qui parse un document à partir d'une adresse url, même si dans ce tutoriel je prendrai un document en local.
Notez cette ligne : [parser setDelegate:self];. On délègue donc la gestion des événements pour le NSXMLParser à XMLToObjectParser. En observant la documentation de NSXMLParser, vous retrouverez les méthodes - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict, - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
et - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string.
Cette classe va donc parcourir le xml. Ensuite, si elle trouve une balise qui porte le nom de la classe pays que nous avons créée, elle copiera les informations et retournera un objet de ce type.
Maintenant, nous retournons dans notre viewController, et on import les .h (
#import "XMLToObjectParser.h" #import "pays.h"
)
Dans la méthode viewDidLoad,
- (void)viewDidLoad {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Monde" ofType:@"xml"];
NSURL *url = [NSURL fileURLWithPath:filePath]; // pour convertir un filePath en NSUrl
XMLToObjectParser *myParser = [[XMLToObjectParser alloc]
parseXMLAtURL:url toObject:@"pays" parseError:nil];
NSLog(@"Pays");
for(int i = 0; i < [[myParser items] count]; i++) {
NSLog(@" %@", [(pays *)[[myParser items] objectAtIndex:i] nom]);
}
[super viewDidLoad];
}Voici ce que vous obtenez :

Affichez maintenant le pays, la capitale, et le nombre d'habitants.
3) Affichons tout ceci dans une TableView
Pour commencer, il nous faut remplir notre TableView :
Dans le rootViewController, nous allons créer un NSMutableArray *tableauPays;
Je vous invite à aller faire un tour sur le tuto de Ceetix si vous n'êtes pas familier avec les TablesView
Ensuite, dans viewDidLoad, nous allons remplir ce tableau avec des objets de type "pays" :
- (void)loadView {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Monde" ofType:@"xml"];
NSURL *url = [NSURL fileURLWithPath:filePath];
XMLToObjectParser *myParser = [[XMLToObjectParser alloc]
parseXMLAtURL:url toObject:@"pays" parseError:nil];
tableauPays = [[NSMutableArray alloc] init];
for(int i = 0; i < [[myParser items] count]; i++) {
pays *nouveauPays;
nouveauPays = (pays *)[[myParser items] objectAtIndex:i];
[tableauPays addObject:nouveauPays];
}
[super viewDidLoad];
}Ensuite, ajoutez une tableView dans ParserXMLViewController.xib. Dans l'onglet "Connections" de la tableView ainsi mise, reliez dataSource et delegate à File's Owner.
Modifiez ensuite ces méthodes :
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tableauPays count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"MyIdentifier"] autorelease];
}
NSString *ligneTableau = [NSString stringWithFormat:@"%@", [[tableauPays objectAtIndex:indexPath.row] nom]];
cell.text=ligneTableau;
return cell;
}
#pragma mark endCompilez, lancez, vous devriez obtenir ceci :

Il serait judicieux d'afficher les informations correspondants à ces pays !
Pour cela, créer une classe UIViewController subclass qui servira à gérer la vue affichant les caractéristiques du pays choisis.
#import <UIKit/UIKit.h>
#import "pays.h"
@interface PaysViewController : UIViewController {
IBOutlet UILabel *nom;
IBOutlet UILabel *capitale;
IBOutlet UILabel *habitants;
pays *unPays;
}
@property (nonatomic, retain) UILabel *nom;
@property (nonatomic, retain) UILabel *capitale;
@property (nonatomic, retain) UILabel *habitants;
@property (nonatomic, retain) pays *unPays;
@end- (void)viewDidLoad {
NSLog(@"viewDidLoad");
nom.text = unPays.nom;
capitale.text = unPays.capitale;
habitants.text = unPays.habitants;
[super viewDidLoad];
}Créons le .xib correspondant :
clic droit sur resources / new File puis

Nommez-la "PaysView"
Ouvrez ensuite ce .xib, spécifiez la classe :

Puis faites les connexions et agencements nécessaires :

Compilez, lancez, il ne se passe rien...
En effet, il faut modifier la méthode suivante :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here -- for example, create and push another view controller.
PaysViewController *paysViewController = [[PaysViewController alloc] initWithNibName:@"PaysView" bundle:nil];
paysViewController.unPays=[tableauPays objectAtIndex:indexPath.row];
self.navigationItem.title=[[tableauPays objectAtIndex:indexPath.row] nom];
[self.navigationController pushViewController:paysViewController animated:YES];
[paysViewController release];
}Et voilà !

Sources
@ bientôt
PS : rajoutez cette ligne pour avoir des flèches :

- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellAccessoryDisclosureIndicator;
}Ipodishima
Copyright © 2009 - ipup.fr • création de Jérémy Lagrue • design de Loann Fraillon • contact