忽略特定节点或分支的最简单解决方案是创建自己的 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