There will always be a need to parse xml, especially if you are dealing with web services. In this post i will show how to use NSXMLParser to parse the xml you get back from Amazon Web Service when you attach a device to a running EC2 instance.
Here is what the xml response looks like:
<AttachVolumeResponse xmlns="http://ec2.amazonaws.com/doc/2010-08-31/"> <volumeId>vol-4d826724</volumeId> <instanceId>i-6058a509</instanceId> <device>/dev/sdh</device> <status>attaching</status> <attachTime>2008-05-07T11:51:50.000Z</attachTime> </AttachVolumeResponse>
To do the parsing , we need to use NSXMLParserDelegate protocol and we will implement three of its methods:
- – parser:didStartElement:namespaceURI:qualifiedName:attributes:
- – parser:didEndElement:namespaceURI:qualifiedName:
- – parser:foundCharacters:
Here is what the header file looks like:
@interface AttachVolumeParser : NSObject {
NSMutableArray *items;
AttachVolumeInfo *volumeInfo;
NSString *keyInProgress;
NSMutableString *textInProgress;
bool isItemInProgress;
}
@property(nonatomic, assign) bool isItemInProgress;
- (BOOL)parseData:(NSData *)d;
- (NSArray *)items;
@end
items is the array that will hold the objects resulting from parsing the xml.
you need to create an object that corresponds to the xml above and that i refer to in above code snippet as AttachVolumeInfo.
And the implementation looks something like this:
#import "AttachVolumeParser.h"
static NSSet *interestingKeys;
@implementation AttachVolumeParser
@synthesize isItemInProgress;
+ (void)initialize
{
if (!interestingKeys) {
interestingKeys = [[NSSet alloc] initWithObjects:@"volumeId", @"instanceId", @"device", @"status", @"attachTime", nil];
}
}
- (void)dealloc
{
[items release];
[super dealloc];
}
- (BOOL)parseData:(NSData *)d
{
[items release];
items = [[NSMutableArray alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:d];
[parser setDelegate:self];
[parser parse];
[parser release];
return YES;
}
- (NSArray *)items
{
return items;
}
#pragma mark Delegate calls
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqual:@"AttachVolumeResponse"]) {
isItemInProgress = true;
volumeInfo = [[AttachVolumeInfo alloc] init];
return;
}
if ([interestingKeys containsObject:elementName]) {
keyInProgress = [elementName copy];
textInProgress = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName isEqual:@"AttachVolumeResponse"]) {
[items addObject:volumeInfo];
[volumeInfo release];
volumeInfo = nil;
isItemInProgress = false;
return;
}
// Is the current key complete?
if ([elementName isEqual:keyInProgress]) {
if([elementName isEqual:@"volumeId"]) {
[volumeInfo setVolumeId:textInProgress];
} else if([elementName isEqual:@"instanceId"]) {
[volumeInfo setInstanceId:textInProgress];
} else if([elementName isEqual:@"device"]) {
[volumeInfo setDevice:textInProgress];
} else if([elementName isEqual:@"status"]) {
[volumeInfo setStatus:textInProgress];
} else if([elementName isEqual:@"attachTime"]) {
[volumeInfo setAttachTime:textInProgress];
}
// Clear the text and key
[textInProgress release];
textInProgress = nil;
[keyInProgress release];
keyInProgress = nil;
}
}
// This method can get called multiple times for the
// text in a single element
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string
{
[textInProgress appendString:string];
}
@end
What i like about this method is that it is fast due to its event-based nature, but on the other hand i need to write a new parser every time i have a new xml schema.
I have already tried to use ObjectiveResource to parse amazon xml schemas but it was a little bit challenging, hopefully i will find some time soon to grab the source code and modify it to do that.
If you have done any amazon xml parsing using a better method, you are welcome to share.
No comments:
Post a Comment