特定のノードまたはブランチを無視する最も簡単な解決策は、独自の XPath のような XML パスを作成し、要素スタックを維持することです。
したがって、新しい要素を解析するたびに、ノードをスタックにプッシュし、現在の XML パスを更新します。次に、終了タグに出くわしたときに、スタックから要素をポップするだけです。
ただし、一部のブランチを無視したい可能性があるため、スタックに一貫性がない可能性があります。いつでも保持して、終了タグの XMLPath とスタックの最後のノードと比較できます。
以下の例では、ドット区切りを使用して XML パスを保存しています。安価で、解析と比較が簡単です。
このアプローチの利点として、いくつかの単純な比較アルゴリズムを実装し、XML 構造の途中にある特定のブランチを無視または一致させながら、特定のノードをより深く掘り下げて解析することができます。
階層の奥深くにある情報でそれらを埋める必要がある場合は、関連するノードをいつでも参照できるため、要素のスタックを持つことは大きなボーナスです。
// 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