NSXMLParser allows parsing XML documents in Objective-C. It provides SAX style event-driven parsing.
Getting Started
Import:
#import <Foundation/Foundation.h>
Initialize:
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
Set delegate:
parser.delegate = self;
Start parsing:
[parser parse];
Delegate Methods
Did start document:
- (void)parserDidStartDocument:(NSXMLParser *)parser {
// Called when parsing starts
}
Did end document:
- (void)parserDidEndDocument:(NSXMLParser *)parser {
// Called when parsing ends
}
Did start element:
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
// Element opened
}
Did end element:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
// Element closed
}
Found characters:
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string {
// Found text
}
Parsing Options
Validate against DTD:
parser.shouldProcessNamespaces = YES;
parser.shouldReportNamespacePrefixes = YES;
parser.shouldResolveExternalEntities = YES;
Progress tracking:
- (void)parser:(NSXMLParser *)parser
parseProgress:(NSUInteger)percentDone {
// percentDone from 0 to 100
}
Validation
Check errors:
NSError *error = [parser parserError];
if(error) {
// handle error
}
Tips
Examples
Simple parsing:
@interface ParserDelegate : NSObject <NSXMLParserDelegate>
@end
@implementation ParserDelegate
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName {
NSLog(@"Started element: %@", elementName);
}
@end
NSData *data = ...; // xml data
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
ParserDelegate *delegate = [[ParserDelegate alloc] init];
parser.delegate = delegate;
[parser parse];
Extract text:
NSMutableString *text;
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName {
text = [[NSMutableString alloc] init];
}
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string {
[text appendString:string];
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName {
NSLog(@"Text: %@", text);
}
Entity Replacement
Custom resolver:
@interface EntityResolver : NSObject <NSXMLParserDelegate>
@end
@implementation EntityResolver
- (NSData *)parser:(NSXMLParser *)parser
resolveExternalEntityName:(NSString *)name
systemIdentifier:(NSString *)systemId {
if (name == ...) {
return replacementData;
} else {
return nil;
}
}
@end
Writing XML
NSXMLDocument:
NSXMLElement *root = [NSXMLElement elementWithName:@"root"];
[doc addChild:root];
NSString *xml = [doc XMLStringWithOptions:0];
NSXMLNode:
NSXMLNode *node = [NSXMLNode elementWithName:@"node"];
[node setStringValue:@"text"];
[root addChild:node];
Concurrent Parsing
Background queue:
dispatch_queue_t queue = dispatch_queue_create("bgQueue", NULL);
parser.delegateQueue = queue;
Thread sync:
@synchronized(self) {
// modify shared state
}
HTML Parsing
Set options:
parser.shouldReportNamespacePrefixes = NO;
parser.shouldResolveExternalEntities = NO;
Check element types:
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
if(namespaceURI == nil) {
// HTML element
}
}
Advanced Usage
Custom stream parsing:
NSInputStream *stream = ...; // custom stream
NSXMLParser *parser = [[NSXMLParser alloc] initWithStream:stream];
// read chunks
[stream open];
while(!done) {
[parser parse];
[stream read];
}
[stream close];
Subclassing:
@interface MyParser : NSXMLParser
// override methods
@end
Additional Examples
Parsing Complex XML
// Recursively parse through elements
- (void)parseElement:(NSXMLElement*)element {
// Process element
for(NSXMLNode* child in element.children) {
if([child isKindOfClass:[NSXMLElement class]) {
[self parseElement:(NSXMLElement*)child];
}
}
}
Handling Errors
- (void)parser:(NSXMLParser *)parser
validateError:(NSError *)parseError
{
if(parseError.code == ...) {
// Handle specific error
} else {
// General error handling
}
}
Concurrent Parsing
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
// Parse in background
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Update UI on main thread
});
Custom Entity Replacement
- (NSData *)parser:(NSXMLParser *)parser
resolveExternalEntityName:(NSString *)name
systemIdentifier:(NSString *)systemId
{
if(name == "foo") {
return [fooData];
} else {
return nil;
}
}
Tips
Core Data Integration
// Parse XML and populate Core Data
NSEntityDescription *entity = ...;
NSManagedObjectContext *context = ...;
NSManagedObject *obj = [NSEntityDescription
insertNewObjectForEntityForName:entity
inManagedObjectContext:context];
obj.title = [currentElement stringValueForAttribute:@"title"];
[context save:&error];
Debugging
Troubleshooting
Invalid XML
Concurrency issues
Memory leaks
Summary of Key Classes and Methods
NSXMLParser - Main parser class
NSXMLParserDelegate - Delegate protocol
NSXMLNode - Represents XML node
NSXMLElement - Subclass of NSXMLNode
External Documentation
Character Encoding
Set
parser.stringEncoding = NSUTF8StringEncoding;