Gestione file XML su iPhone / iPod Touch (BubbleTouch)
by Stefano Lodu
Creare un gioco per iPhone, come per le diverse piattaforme che utilizziamo quotidianamente, non vuol dire soltanto conoscere lo specifico linguaggio che ci serve per creare il codice sorgente e da qui produrre l’eseguibile del gioco: vuol dire prendere atto di una svariata gamma di tecnologie, di sistemi, di linguaggi e pseudo-linguaggi che serviranno ad implementare aspetti multimediali e tecnici come la grafica, le animazioni, il video, la musica, a volte la fisica e la gestione dei dati come ad esempio il salvataggio dei records !. Oggi PiXy ci parlerà di XML… cos’e’ e a cosa può servire !.
1. Cos’e’ XML ?
XML (eXtensible Markup Language) e’ un linguaggio (o meglio un meta-linguaggio) di markup (come ad esempio HTML, utilizzato per strutturare le pagine web)
che puo’ essere utilizzato per memorizzare e gestire dati di qualunque tipo.
Come per il linguaggio HTML un file XML e’ quindi composto da una serie di tag.
Un tag, nel nostro caso, non rappresenta la firma di un writers, ma e’ un elemento sintattico che serve
per strutturare un documento XML (o anche HTML).
Un tag ha sempre un nome, puo’ avere degli attributi, e contenere anche dei dati (caratteri).
Alcune piccole regole:
- i tag non possono iniziare con numeri o caratteri speciali e non possono contenere spazi;
- i tag devono essere bilanciati, ogni tag aperto deve essere chiuso e non sono consentiti errori di annidamento, ad esempio:
<rubrica>
<nome>Mario</nome>
<cognome>Rossi
</rubrica>
il tag cognome non e’ stato chiuso quindi l’XML non e’ ben formato.
Un tag generico ha quindi la seguente sintassi:
<nometag attributo1=”valore1″ attributo2=”valore2″> caratteri… </nometag>
oppure, in forma abbreviata (nel caso in cui il tag non contenga nessun dato/carattere) :
<nometag attributo1=”valore1″ attributo2=”valore2″ />
In un file XML i tag non appartengono ad un insieme prefissato e limitato (come nel caso dell’HTML)
ma possono essere di qualunque tipo e forma in base all’esigenze dell’utente.
La prima riga di un file XML, chiamata solitamente prologo,
serve a definire la versione in uso e la specifica standard per la sua corretta
implementazione.
Infine ogni file XML deve contenere un unico elemento che racchiude tutti gli altri (chiamato elemento radice (root)).
Un esempio completo di un XML ben formato e’ il seguente:
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<utenti>
<utente>
<nome>Luca</nome>
<cognome>Ruggero</cognome>
<indirizzo>Milano</indirizzo>
</utente>
<utente>
<nome>Max</nome>
<cognome>Rossi</cognome>
<indirizzo>Roma</indirizzo>
</utente>
</utenti>
2. Quando posso utilizzare l’XML all’interno di un’applicazione per iPod Touch/iPhone ?
Per la gestione dei dati tre sono le possibili alternative, entrambi molto semplici da utilizzare:
1. Utilizzare file XML
2. Utilizzare file sqlite (database)
3. Utilizzare i file di plist (property list)
Mentre le property list sono piu’ appropriate per salvare le opzioni dell’applicazione, e l’utilizzo di un file
sqlite, quindi di un database, e’ consigliato quando la mole di dati e’ molto vasta ed e’ necessario effettuare delle ricerche,
l’XML puo’ essere utilizzato per piccole quantita’ di dati da memorizzare, o per comunicare con applicazione esterne.
Nel nostro gioco (Bubble Touch), per fare un esempio concreto, stiamo utilizzando file XML per gestire la configurazione dei livelli (tipi di palline presenti,
bonus, malus, oggetti presenti sullo sfondo, obiettivo del livello) e anche lo stato del gioco (livelli completati, punteggi).
Questo ci permette di creare e modificare il comportamento di un livello semplicemente editando un file XML senza la necessita’ di dover
ricompilare ogni volta tutta l’aaplicazione.
Inoltre in questo modo potremmo benissimo creare un editor di livelli (su Mac o anche su PC) che permette di gestire i parametri di un livello
attraverso un’interfaccia grafica e salvarli poi nel formato XML (che ricordiamo e’ semplicemente un file di testo).
3. Come si usa ?
Per scrivere un file XML basta un qualunque editor di testo, cosi’ come a livello di codice, basta preparsi una stringa e salvarla poi su file.
Solitamente i file XML hanno un estensione .xml.
Per leggere un file XML e’ necessario effettuare il riconoscimento dei tag, dei loro attributi e dei caratteri che li contengono.
Come durante la compilazione di un linguaggio di programmazione devono essere riconosciuti i costrutti, le istruzioni e verificata la sintassi, allo
stesso modo dobbiamo fare noi con il nostro file XML.
La verifica della correttezza e della validita’ del file XML (fase successiva) la tralasciamo in quanto richiederebbe maggiori spiegazioni.
4. Parsing
La prima fase, riconoscere i tag, gli attributi e i dati, viene chiamata (come per i linguaggi di programmazione) parsing.
Il parsing analizza una stringa e cerca di ricavare la sua struttura grammaticale, il parser e’ l’oggetto che si occupa di fare questa operazione.
Per poter effettuare il parsing di un file XML solitamente si utilizzano due differenti tipi di interfacce, presenti ed implementate
nella maggior parte dei linguaggi di programmazione:
1. SAX (Simple API for XML)
2. DOM (Document Object Model)
Cocoa Touch mette a disposizione una classe NSXMLParser che permette di analizzare XML utilizzando l’interfaccia SAX.
In Cocoa esiste anche una classe NSXMLDocument che permette di utilizzare l’interfaccia DOM ma non e’ stata inclusa nelle librerie per iPhone (Cocoa Touch).
Sono presenti inoltre molte altre librerie esterne (lib2xml, touchXML, TouchJSON) che permettono di utilizzare anche l’interfaccia DOM e hanno
alcuni vantaggi in termini di perfomance.
5. Il parser SAX : NSXMLParser
NSXMLParser e’ una classe figlia di NSObject (ovviamente) che, come acccennato ci permette di utilizzare l’interfaccia SAX.
Questa interfaccia lavora, leggendo il file XML linea per linea ed e’ basata su eventi.
Durante la lettura al verificarsi di un evento (ad esempio: apertura di un tag, chiusura di un tag, etc..) viene chiamata una funzione specifica
che puo’ gestirlo. Tale funzione prende il nome di handler.
I vantaggi dell’interfaccia SAX sono principalmente legati alla velocita’ e all’utilizzo di memoria (pensate che l’interfaccia DOM costruisce un vero e proprio albero
in memoria che contiene tutta la struttura XML). Con tale interfaccia non e’ possibile scrivere file XML ma solamente leggerli e processarli.
Allora NSXMLParser, essendo un parser SAX, itererà tra tutti gli elementi presenti all’interno del file XML.
Tecnicamente utilizza un delegato per invocare i metodi al verificarsi degli eventi descritti precedentemente.
Ci sono tre metodi che bisogna implementare nella classe che dovrà leggere il file XML:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
Come suggerito dal loro nome didStartElement viene chiamato quando si incontra l’inizio di un tag, didEndElement alla fine di un tag, e foundCharaters serve per poter
ottenere il dato (solitamente caratteri) contenuto all’interno di un elemento (tag).
Incominciamo dal primo metodo: didStartElement
Questo metodo viene chiamato ogni volta che il parser incontra un nuovo tag.
Il puntatore elementName ci dà il nome dell’elemento. Se abbiamo ad esempio un elemento del tipo
<Nominativo Nome=”Angelo Cognome=”Moriconi”></nominativo>
o in forma abbreviata,
<Nominativo Nome=”Angelo” Cognome=”Moriconi />
che non contiene caratteri e ha due attributi, il codice all’iterno di didStartElement per poter prendere il valore dei due attributi sarà il seguente:
if([elementName isEqualToString:@"Nominativo"])
{
std::string nome = [[attributeDict objectForKey:@"Nome"] cString];
std::string cognome = [[attributeDict objectForKey:@"Cognome"] cString];
}
Come potete vedere attributeDict e’ un dizionario che contiene la lista di tutti gli attributi, e con il metodo objectForKey:key possiamo attingere al valore dell’attributo con chiave key.
Tale metodo ci ritorna un oggetto NSString. Nell’esempio tale oggetto è stato convertito in una stringa std (libreria standard del linguaggio c++).
Quindi quello che faremo all’interno di didStartElement solitamente si tradurrà in una serie di if in cascata (per controllare l’inizio di ogni elemento) dove, una volta riconosciuto il tag dato il suo nome possiamo procedere alla memorizzazione dei dati.
Nell’esempio non facciamo assolutamente nulla, possiamo comunque ricostruire la nostra struttura dati passo dopo passo all’interno di questo metodo.
Il metodo didEndElement viene invece utilizzato in maniera simmetrica quando arriva un evento di tag chiuso.
Utilizzato in combinazione con didStartElement ci permette di capire quando abbiamo finito di memorizzare i nostri dati e possiamo finalmente salvarli sulla nostra struttra.
Se ad esempio abbiamo un XML del tipo:
<DatiAnagrafici>
<Nome value=”Angelo” />
<Cognome value=”Moriconi” />
…
…
</DatiAnagrafici>
Possiamo memorizzarci tutti i dati in delle variabili, e, quando l’elemento DatiAnagrafici è terminato possiamo poi salvarci la nostra struttura sulle classi che stiamo utilizzando nella nostra applicazione.
Il metodo foundCharacters viene chiamato anche più di una volta all’interno dello stesso tag, conviene quindi utilizzare una NSMutableString
che deve essere rinizializzata ad ogni nuovo elemento e chiamare il metodo appendString: per poter memorizzare correttamente tutto il contenuto presente all’interno di un elemento.
Come facciamo a creare una classe ed utilizzare i delegati per leggere il file XML ?
Molto semplicemente basta definire una classe figlia di NSObject che può avere anche un solo metodo:
@interface XMLReader: NSObject
- (BOOL)parseXMLFile:(NSString *)URL;
@end
Nel file .m possiamo poi implementare il metodo parseXMLFile facendo in modo che la nostra classe XMLReader sia un delegato di NSXMLParser:
- (BOOL)parseXMLFile:(NSString *)URL
{
//Bisogna convertire il file in una NSURL altrimenti non funziona
NSURL *xmlURL = [NSURL fileURLWithPath:URL];
// Creiamo il parser
NSXMLParser *parser = [[ NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Il delegato del parser e’ la classe stessa (self)
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO]; // Non utilizziamo namespace
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO]; // Vogliamo solamente i dati e non altro…
// Effettuiamo il parser
[parser parse];
// Rilasciamo l’oggetto NSXMLParser
[parser release];
return YES;
}
Infine, sempre nel file .m dovremo implementare i 3 metodi sopra descritti:
- (void)parser:(NSXMLParser *)parser
didStartElement:(N SString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
NSLog(@”Inizio elemento: %@”, elementName);
}
//—————————————————————————-
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
NSLog(@”Fine Elemento: %@”, elementName);
}
//—————————————————————————-
// ATT: Questo metodo viene essere chiamato più volte all’interno dello stesso elemento
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)ch
{
NSLog(@”carattere: %@”, ch);
}
Esiste poi un ulteriore metodo che ci permette di scovare eventuali errori durante il parser:
//—————————————————————————-
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(@”ERROR:%@”, [parseError localizedDescription]);
}
Con questo è tutto !
PiXy / PixMapBrothers e un ringraziamento da sir. Lodux !




7 Comments. Subscribe to this post comments or trackback.
marzo 3rd, 2009
= SeismicXML
marzo 8th, 2009
Davvro un’ottimo tutorial, complimenti a tutti!!
maggio 19th, 2009
Non riesco a capire bene alcune cose ke credo fondamentali:
1. Di che tipo sono le variabili in cui memorizzi i risultati
2. In quali file vai ad implementare i frammenti di codice qui riportati?
3. Perchè non alleghi, x favore, un progetto in cui implementi questa funzionalità?
Ti ringrazio in anticipo x la risposta.
Teo
luglio 13th, 2009
Io come teo non ho capito come utilizarle queste funzioni.. ci puoi aiutare?
agosto 8th, 2010
Essere concordate con Universita iniziative specifiche quali ad attivita obbligatoria di pagare generera il deposito nel rating dei quali la maggioranza relativa delle riserve. Flussi futuri pagamenti poligrafico Zecca dei maggiori punti di forza del prodotto e che la Postepay Twin “va incontro alle esigenze di economicita del trasferimento di denaro e di velocita: infatti.
[URL="http://wukaluj.t35.com/alice-business-gate.html"]Alice business gate[/URL]
agosto 16th, 2010
Viene associata alla prima consegnata al costo di Cavour, attraverso emissione verra crediti ritenuti inesigibili devono essere cancellati dall attivo di legge depositata del trasferimento di esemplari foglio cinquanta esemplari foglio cinquanta esemplari foglio cinquanta esemplari foglio cinquanta esemplari foglio cinquanta esemplari foglio cinquanta esemplari foglio.
[URL="http://caguxof.t35.com/vallebona-lavoro.html"]Vallebona lavoro[/URL]
novembre 15th, 2011
E’ possibile salvare dei dati che inserisco dall’iPhone su un file XML?