2009-05-08 18 views
5

Ho familiarizzato con NSXMLParser dall'SDK di iPhone, ma trovo la natura legata agli eventi di ciò scomodo per i miei scopi. Voglio solo estrarre alcuni valori di elemento ma questo concetto di dover gestire startElement, foundCharacters e endElement sembra più lavoro di quanto dovrebbe essere. Sto solo guardando questo nel modo sbagliato o c'è un modo più semplice per lavorare con XML in tree/DOM nell'SDK di iPhone?Il miglior approccio per l'analisi XML sull'iPhone

Se il consiglio è solo lavorare con NSXMLParser, ci sono alcuni schemi di progettazione che posso usare per mantenere il mio codice da avere 5 livelli di if annidati nel metodo startElement?

risposta

10

Se siete su iPhone, utilizzando l'analisi ad albero può essere uno spreco di memoria proibitivo. Fidati di me, ci sono stato, e ho provato molti approcci diversi negli ultimi cinque mesi di sviluppo della mia applicazione principale per iPhone. L'analisi ad albero funziona bene fino a quando non scarichi il flusso di commenti di qualcuno che contiene 400 commenti molto lunghi, con un clock di circa 600 KB di dati grezzi. A parte le dimensioni dell'albero XML risultante, la memoria allocata internamente durante la creazione di quell'albero può essere enorme.

Finii creando una variante di NSXMLParser che estrae dati da un NSInputStream fornito piuttosto che utilizzare un singolo blocco di dati, e che passa solo 1 KB alla volta nel libxml per la movimentazione (NSXMLParser utilizza libxml troppo, ma passa il 100% dei dati in una volta sola).

Il codice sorgente è disponibile on github (cercare nella cartella StreamingXMLParser). Troverai anche una superclasse delegata; per la maggior parte delle esigenze di analisi è possibile creare sottoclassi AQXMLParserDelegate e implementare -start[Element]WithAttributes: (NSDictionary *) attrs e -end[Element] nella sottoclasse. Questi metodi verranno chiamati per te quando vengono scoperti i tag di inizio e fine e all'interno del tag di fine è possibile utilizzare self.characters per accedere ai caratteri di contenuto o CDATA dell'elemento.

Per ulteriori sulle impronte di memoria relativi dei diversi parser (anche se su Mac, non l'iPhone) Guarda i miei post originale here e il follow-up su NSXMLDocument here.

+0

Grazie per informazioni utili. Ho finito con l'adozione di startElement, foundCharacters, endElement pattern e non era male ma si, ora sto notando che initWithContentsOfURL di NSXMLParser sembra scaricare l'intero documento e lasciarlo in memoria anziché farlo streaming, come hai fatto notare. Il che è piuttosto sorprendente poiché non vi è alcun motivo per cui sia necessario accedere all'intero documento quando si utilizza un approccio di analisi basato su eventi. Guarderò StreamingXMLParser. – Marplesoft

+0

Ok ulteriori indagini. Ora sto notando che l'impronta della memoria è peggiorata a causa del download dell'URL rispetto all'effettivo parsing. Sto facendo un download asincrono ma non sembra che stia rilasciando i blocchi di dati già ricevuti. – Marplesoft

+0

Sì, la roba NSURLConnection alloca internamente un bel po 'di memoria mentre fa le cose - e se stai usando SSL ci sono ~ 1MB extra allocati per la pipeline di crittografia. Ho finito per scrivere il mio wrapper su CFHTTPMessageRef e usarlo per ottenere un flusso per alimentare il parser; si trova nello stesso repository github, nella sottocartella HTTPMessage. –

1

Considerare il seguente frammento di codice, che utilizza libxml2, Matt Gallagher's libxml2 wrappers e Ben Copsey's ASIHTTPRequest per analizzare un documento XML.

L'istanza di NSArray* contiene NSDictionary* oggetti che è possibile analizzare in modo ricorsivo per ottenere i dati desiderati.

Oppure, se si conosce lo schema del documento XML, è possibile scrivere una query XPath per ottenere direttamente un valore nodeContent o nodeAttribute.

ASIHTTPRequest *request = [ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://stackoverflow.com/"]; 
[request start]; 
NSError *error = [request error]; 
if (!error) { 
    NSData *response = [request responseData]; 
    NSLog(@"Root node: %@", [[self query:@"//" withResponse:response] description]); 
} 
else 
    @throw [NSException exceptionWithName:@"kHTTPRequestFailed" reason:@"Request failed!" userInfo:nil]; 
[request release]; 

... 

- (id) query:(NSString *)xpathQuery withResponse:(NSData *)respData { 
    NSArray *nodes = PerformXMLXPathQuery(respData, xpathQuery); 
    if (nodes != nil) 
     return nodes; 
    return nil; 
} 
0

La riconversione del codice da XML sismico fornisce un'API molto buona che crea sottoclassi NSObject da XML.

Se il consiglio è quello di lavorare solo con NSXMLParser, ci sono alcuni modelli di progettazione che posso utilizzare per mantenere il mio codice di avere 5 livelli di if nidificati nel metodo startElement?

Dipendo da ciò che si sta tentando di fare.È possibile inserire i nomi degli elementi in un dizionario e agire in base all'oggetto pertinente in un dizionario: ciò è effettivamente ciò che fa SeismicXML.

Problemi correlati