Andrea "Kiraya"Magini

IT Professional Master

Single post

NSXmlParser il default XmlParser di iOS

Per chi lavora da molto con la programmazione, sia Java che .Net, avere a disposizione un Dom per la lettura dei files xml tramite un documento a grafo e magari espressioni xpath, è oramai legge.
Eppure per lavorare su IPhone tramite iOS e in generale su Mac OSX, di default troviamo solo ed esclusivamente un SaxParser, l’NSXmlParser..
Superato l’impatto delle Sax (da sempre ostiche ma sicuramente molto performanti), facciamo un velocissimo esempio di come usarle in una nostra classe, per leggere velocemente dei dati da un file xml.
Innanzitutto aggiungiamo l’interfaccia  NSXMLParserDelegate alla classe che riceverà gli eventi Sax, perche le Sax lavorano completamente ad eventi. Quando verrà lanciato il parser, scatteranno eventi ogni volta che si verificherà una condizione (aperto nodo, presenza testo, chiuso nodo, etc etc etc )

@interface MiaClasse : NSObject 

in questa classe (che può essere una qualsiasi classe del nostro progetto, dichiariamo l’xml parser nell’header , l’elemento che conterrà l’array di dati che andiamo ad estrarre, i puntatori per i singoli valori (on the fly come variabili dirette, ma in genere meglio definire un oggetto Item che esponga tutti gli attributi), il puntatore all’elemento xml corrente che il parser sta analizzando:

@interface MiaClasse : NSObject 
//parser
NSXMLParser *xmlParser;
//array che conterrà i dati
NSMutableArray *elencoItems;
//variabile temporanea per ogni elemento
NSMutableDictionary *item;// nome del nodo corrente che il sax parser sta analizzando
NSString *currentElement;
//elementi che leggeremo e poi memorizzeremo
NSMutableString *nome, *cognome, *indirizzo;

ora che abbiamo definito l’header, passiamo all’implementazione.
Inizializziamo il parser e l’array di dati nel metodo in cui lo andiamo ad usare, nel costruttore o dove vogliamo.
In questo caso andiamo a leggere il file xml da risorsa locale, ma possiamo accedere tranquillamente ad un file remoto .

- (void)applicationDidFinishLaunching:(UIApplication *)application {
elencoItems= [[NSMutableArray alloc] init];
xmlParser = [[NSXMLParser alloc] initWithData:[NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"test.xml"]  ]];
//definiamo il delegato che riceverà gli eventi (in questo caso la classe stessa
[xmlParser setDelegate:self];
//saltiamo i problemi di namespace
[xmlParser setShouldProcessNamespaces:NO];
[xmlParser setShouldReportNamespacePrefixes:NO];
[xmlParser setShouldResolveExternalEntities:NO];
// avviamo il parsing XML
[xmlParser parse];
}

ora definiamo i metodi essenziali per soddisfare l’interfaccia NSXMLParserDelegate:
didStartElement , che viene eseguito ad ogni inizio di elemento xml trovato
didEndElement, che viene eseguito quando viene analizzata la chiusura di un tag xml
foundCharacters, che viene eseguito quando si analizza il contenuto di tipo “text” di un elemento
parserDidEndDocument, che scatta quando si raggiunge la fine del documento xml.

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
//qui mettiamo tutte le regole di match del nostro xml, ad esempio cosa fare quando
// si incontra un nodo denominato "persona"
if ([elementName isEqualToString:@"persona"]) {
// inizializza tutti gli elementi che conterranno i dati. memorizzeremo ogni item come tabella per praticità
item = [[NSMutableDictionary alloc] init];
nome = [[NSMutableString alloc] init];
cognome = [[NSMutableString alloc] init];
indirizzo = [[NSMutableString alloc] init];
}
}

Nel metodo didEndElement andremo fisicamente a prendere i dati letti e a memorizzarli in una struttura (il nostro array)

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:@"persona"]) {
[item setObject:nome forKey:@"nome"];
[item setObject:cognome forKey:@"cognome"];
[item setObject:indirizzo forKey:@"indirizzo"];
[elencoItems addObject:[item copy]];
}
}

Ed ora il metodo che fisicamente legge i dati dai nodi:

 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([currentElement isEqualToString:@"nome"]){
[nome appendString:string];
} else if ([currentElement isEqualToString:@"cognome"]) {
[cognome appendString:string];
} else if ([currentElement isEqualToString:@"indirizzo"]) {
[indirizzo appendString:string];
} }

Infine se a termine del traversing del grafo xml dobbiamo effettuare delle operazioni, come prendere il risultato e passarlo ad altri oggetti, utilizziamo il parserDidEndDocument:

- (void)parserDidEndDocument:(NSXMLParser *)parser {
[mioOggetti setData:elencoItems];
[elencoItems release];
}

In linea di massima e’ molto semplice utilizzare XmlParser.
Come tutti i framework e metodologie su iOS si fa un grande uso dei delegati.
Spero di esservi stato di aiuto.