【问题标题】:NSXMLParser ignore nodesNSXMLParser 忽略节点
【发布时间】:2012-04-26 18:15:50
【问题描述】:

我在要阅读的 xml 中有以下操作。但我只想阅读<goodactions><niceactions>。我需要在didStartElement 委托函数中放置标志吗?

<goodactions>
  <action name=\"play\" id=\"0\"/>
  <action name=\"eat\" id=\"1\" />
  <action name=\"sleep\" id=\"2\" />
  <action name=\"study\" id=\"3\"/>
</goodactions>
<badactions>
  <action name=\"smoke\" id=\"0\"/>
  <action name=\"watch_tv\" id=\"1\"/>
</badactions>
<niceactions>
  <action name=\"cycling\" id=\"0\"/>
  <action name=\"swimming\" id=\"1\"/>
</niceactions>

【问题讨论】:

    标签: objective-c ios nsxmlparser


    【解决方案1】:

    也许您要问其他问题,但一个简单的布尔值可能就足够了。

    BOOL ignoreElement; 
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    
        if ([elementName isEqualToString:@"badactions"]) {
    
            self.ignoreElement = YES; 
    
        }
    }
    
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    
        if (!self.ignoreElement) {
            // data from other elements
        }
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    
        if ([elementName isEqualToString:@"badactions"]) {
    
            self.ignoreElement = NO; 
    
        }
    
    }
    

    【讨论】:

    【解决方案2】:

    是的,我就是这样做的。当您的委托的 -parser:didStartElement:::: 方法被调用时,如果元素名称为“badactions”,请设置一个标志以忽略所有内容。然后在-parser:didEndElement: 中,如果元素名称为“badactions”,则重置标志。像这样的:

    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
        if ([elementName isEqualToString:@"badactions"])
        {
            _ignoreElement = YES;
            return;
        }
        if (!_ignoreElement)
        {
             // handle other elements here
        }
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
        // Reset if we were ignoring a badactions element
        if (([elementName isEqualToString:@"badactions"]) && (_ignoreElement))
        {
             _ignoreElement = NO;
        }
    }
    

    【讨论】:

    • 这会排除 badactions 下的操作吗?还是只是元素 badactions?
    【解决方案3】:

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

    【讨论】: