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.
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";
}
Check out this tutorial. It explains how to use the NSXMLParser to parse data from XML into your own data format.