2009-05-08 28 views
5

Me he familiarizado con NSXMLParser desde el iPhone SDK, pero considero que la naturaleza del evento es incómoda para mis propósitos. Solo quiero extraer algunos valores de los elementos, pero este concepto de tener que manejar el startElement, foundCharacters y endElement parece más trabajo de lo que realmente debería ser. ¿Acabo de ver esto de la manera incorrecta o hay una manera más simple de trabajar con XML basada en DOM/DOM en el SDK de iPhone?Mejor enfoque para el análisis de XML en el iPhone

Si el consejo es simplemente trabajar con NSXMLParser, ¿hay ciertos patrones de diseño que pueda usar para evitar que mi código tenga 5 niveles de if anidados en el método startElement?

Respuesta

10

Si estás en el iPhone, usando análisis basado en árboles puede ser un cerdo de la memoria prohibitivo. Confíe en mí, he estado allí, y he intentado muchos enfoques diferentes en los últimos cinco meses de desarrollo de mi aplicación principal de iPhone. El análisis basado en árboles funciona bien hasta que descarga la secuencia de comentarios de alguien que contiene 400 comentarios muy largos, registrando aproximadamente 600 KB de datos sin formato. Aparte del tamaño del árbol XML resultante, la memoria asignada internamente al crear ese árbol puede ser enorme.

I terminaron crear una variante de NSXMLParser que tira de datos desde un NSInputStream suministrado en lugar de utilizar un único fragmento de datos, y que pasa solamente de 1 KB a la vez en libxml para el manejo (NSXMLParser utiliza libxml también, pero pasa el 100% de los datos de una vez).

El código fuente está disponible on github (buscar en la carpeta StreamingXMLParser). También encontrarás una superclase de delegado allí; para la mayoría de las necesidades de análisis, puede subclase AQXMLParserDelegate e implementar -start[Element]WithAttributes: (NSDictionary *) attrs y -end[Element] en su subclase. Se buscarán estos métodos cuando se descubran las etiquetas de inicio y fin, y dentro de la etiqueta del final puede usar self.characters para acceder a los caracteres de contenido o CDATA del elemento.

Para más información sobre las huellas de memoria relativas de los diferentes programas de análisis (aunque en el Mac, no el iPhone) ver mi post original del blog here y el seguimiento de NSXMLDocument here.

+0

Gracias esta es información útil. Terminé adoptando el patrón startElement, foundCharacters, endElement y no fue tan malo, pero sí ahora me doy cuenta de que NSXMLParser initWithContentsOfURL parece descargar todo el documento y dejarlo en la memoria en lugar de transmitirlo, como usted señaló. Lo cual es algo sorprendente ya que no hay ninguna razón por la que necesite acceder al documento completo cuando usa un enfoque de análisis basado en eventos. Veré en StreamingXMLParser. – Marplesoft

+0

Ok más investigación. Ahora me doy cuenta de que la huella de memoria es más debido a la descarga de URL que el análisis real. ¿Estoy haciendo una descarga asíncrona pero parece que no está liberando los fragmentos de datos ya recibidos? – Marplesoft

+0

Sí, el material NSURLConnection asigna un poco de memoria internamente mientras está haciendo cosas, y si está utilizando SSL hay ~ 1MB extra asignado para la canalización de encriptación. Terminé escribiendo mi propio envoltorio alrededor de CFHTTPMessageRef y usándolo para obtener un flujo para alimentar el analizador; eso está en el mismo repositorio github, en la subcarpeta HTTPMessage. –

1

Considere el siguiente fragmento de código, que usa libxml2, Matt Gallagher's libxml2 wrappers y Ben Copsey's ASIHTTPRequest para analizar un documento XML.

La instancia nodes del tipo NSArray* contendrá NSDictionary* objetos que puede analizar de forma recursiva para obtener los datos que desea.

O, si conoce el esquema de su documento XML, puede escribir una consulta XPath para obtener un valor nodeContent o nodeAttribute directamente.

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 reutilización del código de Seismic XML proporciona una API muy buena que crea subclases de NSObject a partir de XML.

Si el consejo es simplemente trabajar con NSXMLParser, ¿hay ciertos patrones de diseño que pueda utilizar para evitar que mi código tenga 5 niveles de if anidados en el método startElement?

Depende de lo que intenta hacer.Podría poner los nombres de sus elementos en un diccionario y tomar medidas basadas en el objeto relevante de un diccionario; esto es lo que efectivamente hace SeismicXML.

Cuestiones relacionadas