The easiest solution to ignore specific nodes or branches is to create your own XPath-like XML path and maintain element stack.
So whenever you parse new element you push node on stack, and update current XML path. Then when you stumble upon closing tag, you simply pop element from stack.
However your stack can be inconsistent because you probably want to ignore some branches, you can always keep and compare against XMLPath of closing tag and last node on stack.
In given example below I store XML path using dot separator. It's cheap and easy to parse and compare.
As a benefit of this approach, you can implement some simple comparison algorithm and ignore or match specific branches in the middle of XML structure but dive and parse out specific nodes deeper.
Having stack of elements is a huge bonus because you can always reference related nodes if you need to fill them up with some information located deep down the hierarchy.
// Base node class
@interface MYNode : NSObject
@property NSString* XMLPath;
@end
// Some custom node subclass
@interface MYRootNode : MYNode @end
// Some other custom node subclass
@interface MYBlockNode : MYNode @end
// NSXMLParserDelegate interface
@interface MYXMLParserDelegate : NSObject<NSXMLParserDelegate>
@property NSMutableArray* elementStack;
@property NSString* XMLPath;
@property MYRootNode* rootNode;
@end
// NSXMLParserDelegate implementation
@implementation MYXMLParserDelegate
#pragma mark - Initializer
- (id)init {
if(self = [super init]) {
self.elementStack = [NSMutableArray new];
}
return self;
}
#pragma mark - XMLPath manipulation methods
- (void)addXMLPathComponent:(NSString*)component {
NSString* newXMLPath;
if(self.XMLPath.length) {
newXMLPath = [self.XMLPath stringByAppendingFormat:@".%@", component];
} else {
newXMLPath = component;
}
self.XMLPath = newXMLPath;
}
- (void)removeLastXMLPathComponent {
NSRange range = [self.XMLPath rangeOfString:@"." options:NSBackwardsSearch];
if(range.location != NSNotFound) {
self.XMLPath = [self.XMLPath substringToIndex:range.location];
}
}
#pragma mark - NSXMLParserDelegate
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
MYNode* node;
// Add XML path component
[self addXMLPathComponent:elementName];
// Process relevant nodes
if([self.XMLPath isEqualToString:@"document.page"])
{
node = [[MYRootNode alloc] initWithAttributes:attributeDict];
// Save root node
self.rootNode = node;
}
else if([self.XMLPath isEqualToString:@"document.page.block"])
{
node = [[MYBlockNode alloc] initWithAttributes:attributeDict];
}
// Push relevant node on stack
if(node) {
node.XMLPath = self.XMLPath;
[self.elementStack addObject:node];
NSLog(@"-> %@", self.XMLPath);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
MYNode* node = [self.elementStack lastObject];
// Remove node from stack if XML path match found
if([node.XMLPath isEqualToString:self.XMLPath]) {
[self.elementStack removeLastObject];
NSLog(@"<- %@", self.XMLPath);
}
// Pop XML path component
[self removeLastXMLPathComponent];
}
@end