views:

3583

answers:

3

I've familiarized myself with the NSXMLParser from the iPhone SDK but I find the event-driven nature of it awkward for my purposes. I just want to extract some element values but this concept of having to handle the startElement, foundCharacters, and endElement seems like more work than it really should be. Am I just looking at this the wrong way or is there a simpler tree/DOM-based way of working with XML in the iPhone SDK?

If the advice is to just work with NSXMLParser, are there certain design patterns I can use to keep my code from having 5 levels of nested ifs in the startElement method?

+1  A: 

Consider the following code snippet, that uses libxml2, Matt Gallagher's libxml2 wrappers and Ben Copsey's ASIHTTPRequest to parse an XML document.

The nodes instance of type NSArray* will contain NSDictionary* objects that you can parse recursively to get the data you want.

Or, if you know the scheme of your XML document, you can write an XPath query to get you to a nodeContent or nodeAttribute value directly.

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;
}
Alex Reynolds
+8  A: 
Jim Dovey
Thanks this is useful info. I ended up adopting the startElement, foundCharacters, endElement pattern and it wasn't too bad but yeah now I'm noticing that NSXMLParser initWithContentsOfURL seems to download the whole document and leave it in memory as opposed to streaming it - like you pointed out. Which is kinda surprising since there's no reason you need access to the whole document when you're using an event-based parsing approach. I'll look into StreamingXMLParser.
Marplesoft
Ok more investigation. Now I'm noticing that the memory footprint is moreso due to the URL download than the actual parsing. Am I doing an async download but it doens't seem to be releasing the already-received data chunks.
Marplesoft
Yeah, the NSURLConnection stuff allocates a fair bit of memory internally while its doing things— and if you're using SSL there's ~1MB extra allocated for the encryption pipeline. I wound up writing my own wrapper around CFHTTPMessageRef and using that to get a stream to feed the parser; that's in the same github repository, in the HTTPMessage subfolder.
Jim Dovey
A: 

Repurposing the code from Seismic XML provides a very good API that creates NSObject subclasses from XML.

If the advice is to just work with NSXMLParser, are there certain design patterns I can use to keep my code from having 5 levels of nested ifs in the startElement method?

I depends on what you are trying to do. You could put your element names in a dictionary and take action based on the relevant object in a dictionary - this is effectively what SeismicXML does.

Roger Nolan