Skip to content

Instantly share code, notes, and snippets.

@mrtj
Last active December 14, 2015 18:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrtj/5132831 to your computer and use it in GitHub Desktop.
Save mrtj/5132831 to your computer and use it in GitHub Desktop.
Simple DOM based XML parser #cocoa #xml #parser #utils #dom
#import <Foundation/Foundation.h>
@interface TJXMLElement : NSObject
@property (nonatomic, readonly) NSString* name;
@property (nonatomic, readonly) NSDictionary* attributes;
@property (nonatomic, retain) NSMutableArray* children;
@property (nonatomic, retain) NSMutableString* text;
- (id) initWithName:(NSString*)name withAttributes:(NSDictionary*)attributes;
- (TJXMLElement*) childWithName:(NSString*)name;
- (NSArray*) childrenWithName:(NSString*)name;
@end
@interface TJXMLParser : NSObject <NSXMLParserDelegate> {
@private
NSXMLParser* _parser;
NSMutableArray* _parentList;
}
- (id) initWithContentsOfURL:(NSURL *)url;
- (id) initWithData:(NSData *)data;
- (TJXMLElement*) parse;
@property (nonatomic, retain) TJXMLElement* currentElement;
@property (nonatomic, retain) TJXMLElement* rootElement;
@property (nonatomic, retain) NSError* parseError;
@end
#import "TJXMLParser.h"
@implementation TJXMLElement
@synthesize name = _name;
@synthesize attributes = _attributes;
@synthesize children = _children;
@synthesize text = _text;
- (id) initWithName:(NSString*)name withAttributes:(NSDictionary*)attributes
{
self = [super init];
if (self != nil) {
_name = [name retain];
_attributes = [attributes retain];
_children = [[NSMutableArray alloc] init];
_text = [[NSMutableString alloc] init];
}
return self;
}
- (void) dealloc
{
[_name release]; _name = nil;
[_attributes release]; _attributes = nil;
self.children = nil;
self.text = nil;
[super dealloc];
}
- (NSString *)description
{
NSMutableString* desc = [[[NSMutableString alloc] initWithString:[super description]] autorelease];
[desc appendFormat:@"\nName: %@\nAttributes: %@\nText: %@\nChildren: %d item(s)\n",
self.name,
[self.attributes count] > 0 ? [self.attributes description] : @"nil",
[self.text length] > 0 ? self.text : @"nil",
[self.children count]];
return desc;
}
- (TJXMLElement*) childWithName:(NSString*)name
{
for (TJXMLElement* child in self.children) {
if ([name isEqualToString:child.name])
return child;
}
return nil;
}
- (NSArray*) childrenWithName:(NSString*)name
{
NSMutableArray* results = [NSMutableArray array];
for (TJXMLElement* child in self.children) {
if ([name isEqualToString:child.name])
[results addObject:child];
}
return results;
}
@end
@interface TJXMLParser ()
- (void) construct;
@end
@implementation TJXMLParser
@synthesize currentElement = _currentElement;
@synthesize rootElement = _rootElement;
@synthesize parseError = _parseError;
#pragma mark Init & dealloc
- (id) initWithContentsOfURL:(NSURL *)url
{
self = [super init];
if (self != nil) {
_parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[self construct];
}
return self;
}
- (id) initWithData:(NSData *)data
{
self = [super init];
if (self != nil) {
_parser = [[NSXMLParser alloc] initWithData:data];
[self construct];
}
return self;
}
- (void) construct
{
_parser.delegate = self;
_parentList = [[NSMutableArray alloc] init];
}
- (void) dealloc
{
[_parser release]; _parser = nil;
[_parentList release]; _parentList = nil;
self.currentElement = nil;
self.rootElement = nil;
self.parseError = nil;
[super dealloc];
}
#pragma mark Parsing
- (TJXMLElement*) parse
{
BOOL result = [_parser parse];
if (result) {
return [[self.rootElement retain] autorelease];
} else {
return nil;
}
}
#pragma mark NSXMLParserDelegate implementation
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.currentElement = nil;
self.rootElement = nil;
self.parseError = nil;
}
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
TJXMLElement* newElement = [[[TJXMLElement alloc] initWithName:elementName withAttributes:attributeDict] autorelease];
if (self.currentElement) {
TJXMLElement* parentElement = self.currentElement;
[_parentList addObject:parentElement];
[parentElement.children addObject:newElement];
} else {
self.rootElement = newElement;
}
self.currentElement = newElement;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[self.currentElement.text appendString:string];
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (![elementName isEqualToString:self.currentElement.name]) {
// this is just to be sure, the parser would report an error before
[parser abortParsing];
}
self.currentElement = [_parentList lastObject];
[_parentList removeLastObject];
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
if (self.currentElement != nil || [_parentList count] > 0) {
// this is just to be sure, the parser would report an error before
[parser abortParsing];
}
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
self.parseError = parseError;
self.rootElement = nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment