iOS: Parsing a xml from HTTP using NSXMLParser for

Go To StackoverFlow.com

0

It's first time using NSXMLParser and wondering if you give me some direction of parsing the returned xml from an http request that looks like:

<?xml version="1.0" ?>
<theresponse>
 <status>OK</status>
 <pricing currency="USD" symbol="$">
   <price class="items">24.00</price>
   <price class="shipping">6.00</price>
   <price class="tax">1.57</price>
 </pricing>
</theresponse>

I know the basic of parsing delegate methods, I just want to know what the code would look like in didEndElement/foundCharacters/didStartElement for retreiving above items(currency/items/shipping/tax)? any help greatly appreciated.

2012-04-05 21:46
by Cam


1

This is a little more tricky than some standard NSXMLParser code; Because essentially when you are looking for the "shipping" you want "6.00" but those two pieces of data are returned to you in different delegate methods, which is normal. But usually the element would be named "shipping" so in parser:didEndElement:namespaceURI:qualifiedName: you would automatically have the element name as it was passed into the method.

The solution would seem simple, have a _currentAttributes ivar and in parser:didStartElement:namespaceURI:qualifiedName:attributes: do something like _currentAttributes = attributeDict; and then handle this in the didEndElement: method. However this style would easily break, even on this moderately simple XML.

My way of handling this would be to store the attributes dictionary passed into the didStartElement: and set it in a dictionary as the object for the key of the element name. Combining this style with the standard use of an NSMutableString as a characterBuffer of sorts allows you to put all of your logic into the didEndElement: method.

Side note: I am also quite fond of having my NSXMLParserDelegate classes be NSXMLParser subclasses, as this one is. However the delegate methods would be identical if it were not.

ItemParser.h

#import <Foundation/Foundation.h>
@interface ItemParser : NSXMLParser <NSXMLParserDelegate>
@property (readonly) NSDictionary *itemData;
@end

ItemParser.m

#import "ItemParser.h"
@implementation ItemParser {
    NSMutableDictionary *_itemData;
    NSMutableDictionary *_attributesByElement;
    NSMutableString *_elementString;
}
-(NSDictionary *)itemData{
    return [_itemData copy];
}
-(void)parserDidStartDocument:(NSXMLParser *)parser{
    _itemData = [[NSMutableDictionary alloc] init];
    _attributesByElement = [[NSMutableDictionary alloc] init];
    _elementString = [[NSMutableString alloc] init];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
    // Save the attributes for later.
    if (attributeDict) [_attributesByElement setObject:attributeDict forKey:elementName];
    // Make sure the elementString is blank and ready to find characters
    [_elementString setString:@""];
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    // Save foundCharacters for later
    [_elementString appendString:string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    if ([elementName isEqualToString:@"status"]){
        // Element status only contains a string i.e. "OK"
        // Simply set a copy of the element value string in the itemData dictionary
        [_itemData setObject:[_elementString copy] forKey:elementName];
    } else if ([elementName isEqualToString:@"pricing"]) {
        // Pricing has an interesting attributes dictionary 
        // So copy the entries to the item data
        NSDictionary *attributes = [_attributesByElement objectForKey:@"pricing"];
        [_itemData addEntriesFromDictionary:attributes];
    } else if ([elementName isEqualToString:@"price"]) {
        // The element price occurs multiple times.
        // The meaningful designation occurs in the "class" attribute.
        NSString *class = [[_attributesByElement objectForKey:elementName] objectForKey:@"class"];
        if (class) [_itemData setObject:[_elementString copy] forKey:class];
    }
    [_attributesByElement removeObjectForKey:elementName];
    [_elementString setString:@""];
}
-(void)parserDidEndDocument:(NSXMLParser *)parser{
    _attributesByElement = nil;
    _elementString = nil;
}
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
    NSLog(@"%@ with error %@",NSStringFromSelector(_cmd),parseError.localizedDescription);
}
-(BOOL)parse{
    self.delegate = self;
    return [super parse];
}
@end

And so to test I stored the XML you posted above into a file named "ItemXML.xml". And tested it using this code:

NSURL *url = [[NSBundle mainBundle] URLForResource:@"ItemXML" withExtension:@"xml"];
ItemParser *parser = [[ItemParser alloc] initWithContentsOfURL:url];
[parser parse];
NSLog(@"%@",parser.itemData);

The result I got was:

{
    currency = USD;
    items = "24.00";
    shipping = "6.00";
    status = OK;
    symbol = "$";
    tax = "1.57";
}
2012-04-08 18:55
by NJones
Thank you so much - Cam 2012-04-09 19:28


0

Check out this tutorial. It explains how to use the NSXMLParser to parse data from XML into your own data format.

2012-04-06 19:02
by joern
Ads