How to use NSXMLParser to parse parent-child eleme

2020-01-26 04:29发布

问题:

Does anyone have some idea how to parse the following xml using event-driven model NSXMLParser class?

<Node>
  <name> Main </name>
  <Node>
    <name> Child 1 </name>
  </Node>

  <Node>
    <name> Child 2 </name>
  </Node>
</Node>

I want to collect all three names from this xml file, is it possible, or I have to change to Tree-based parsing?

回答1:

This is a common problem with parsers like this one, of "type SAX", where you have to manually keep track of the current depth of the XML tree you're in. The problem, as always, is that loading the entire tree in a DOM structure in memory can be impossible, depending on the size of the data you want to manipulate.

The following code shows a class that does this job:

#import <Foundation/Foundation.h>

@interface Test : NSObject <NSXMLParserDelegate> 
{
@private
    NSXMLParser *xmlParser;
    NSInteger depth;
    NSMutableString *currentName;
    NSString *currentElement;
}

- (void)start;

@end

This is the implementation:

#import "Test.h"

@interface Test ()
- (void)showCurrentDepth;
@end


@implementation Test

- (void)dealloc
{
    [currentElement release];
    [currentName release];
    [xmlParser release];
    [super dealloc];
}

- (void)start
{
    NSString *xml = @"<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Node><name>Main</name><Node><name>Child 1</name></Node><Node><name>Child 2</name></Node></Node>";
    xmlParser = [[NSXMLParser alloc] initWithData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
    [xmlParser setDelegate:self];
    [xmlParser setShouldProcessNamespaces:NO];
    [xmlParser setShouldReportNamespacePrefixes:NO];
    [xmlParser setShouldResolveExternalEntities:NO];
    [xmlParser parse];

}

#pragma mark -
#pragma mark NSXMLParserDelegate methods

- (void)parserDidStartDocument:(NSXMLParser *)parser 
{
    NSLog(@"Document started", nil);
    depth = 0;
    currentElement = nil;
}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError 
{
    NSLog(@"Error: %@", [parseError localizedDescription]);
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qName 
    attributes:(NSDictionary *)attributeDict
{
    [currentElement release];
    currentElement = [elementName copy];

    if ([currentElement isEqualToString:@"Node"])
    {
        ++depth;
        [self showCurrentDepth];
    }
    else if ([currentElement isEqualToString:@"name"])
    {
        [currentName release];
        currentName = [[NSMutableString alloc] init];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qName
{

    if ([elementName isEqualToString:@"Node"]) 
    {
        --depth;
        [self showCurrentDepth];
    }
    else if ([elementName isEqualToString:@"name"])
    {
        if (depth == 1)
        {
            NSLog(@"Outer name tag: %@", currentName);
        }
        else 
        {
            NSLog(@"Inner name tag: %@", currentName);
        }
    }
}        

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if ([currentElement isEqualToString:@"name"]) 
    {
        [currentName appendString:string];
    } 
}

- (void)parserDidEndDocument:(NSXMLParser *)parser 
{
    NSLog(@"Document finished", nil);
}

#pragma mark -
#pragma mark Private methods

- (void)showCurrentDepth
{
    NSLog(@"Current depth: %d", depth);
}

@end

This is the result of running a command line tool that triggers the "start" method above:

Document started
Current depth: 1
Outer name tag: Main
Current depth: 2
Inner name tag: Child 1
Current depth: 1
Current depth: 2
Inner name tag: Child 2
Current depth: 1
Current depth: 0
Document finished


回答2:

- (NSInteger)columnNumber

gives you the nesting level of NSXMLParser



回答3:

A very easier way to solve it than the accepted answer, for short XML files:

XML example:

<cotacoes>
    <bovespa>
            <cotacao>50058</cotacao>
            <variacao>-0.16</variacao>
    </bovespa>
    <dolar>
            <cotacao>3.4610</cotacao>
            <variacao>+0.29</variacao>
    </dolar>
    <euro>
            <cotacao>3.7673</cotacao>
            <variacao>-0.25</variacao>
    </euro>
    <atualizacao>04/08/15 - 18:14</atualizacao>
</cotacoes>

Implement a counter:

@implementation className{
    int parserCounter;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    parserCounter++;

    NSLog(@"%d: %@",contadorParser,string);

    if (parserCounter == 3) doAnythingFor3;
    if (parserCounter == 5) doAnythingFor5;
    if (parserCounter == 9) doAnythingFor9;
    if (parserCounter == 11) doAnythingFor11;
    if (parserCounter == 15) doAnythingFor15;
    if (parserCounter == 17) doAnythingFor17;
    if (parserCounter == 20) doAnythingFor20;

}

How will i know witch number is witch node ? By seeing the log. It will be something like:

2015-08-04 23:36:08.070 Tesouro Direto[7252:1554402] 1:     
2015-08-04 23:36:08.070 Tesouro Direto[7252:1554402] 2:     
2015-08-04 23:36:08.071 Tesouro Direto[7252:1554402] 3:     50058
2015-08-04 23:36:08.105 Tesouro Direto[7252:1554402] 4:     
2015-08-04 23:36:08.105 Tesouro Direto[7252:1554402] 5:     -0.16
2015-08-04 23:36:08.131 Tesouro Direto[7252:1554402] 6:     
2015-08-04 23:36:08.132 Tesouro Direto[7252:1554402] 7:     
2015-08-04 23:36:08.132 Tesouro Direto[7252:1554402] 8:     
2015-08-04 23:36:08.132 Tesouro Direto[7252:1554402] 9:     3.4610
2015-08-04 23:36:08.156 Tesouro Direto[7252:1554402] 10:    
2015-08-04 23:36:08.156 Tesouro Direto[7252:1554402] 11:    +0.29   
2015-08-04 23:36:08.180 Tesouro Direto[7252:1554402] 12:    
2015-08-04 23:36:08.180 Tesouro Direto[7252:1554402] 13:    
2015-08-04 23:36:08.180 Tesouro Direto[7252:1554402] 14:    
2015-08-04 23:36:08.180 Tesouro Direto[7252:1554402] 15:    3.7673
2015-08-04 23:36:08.203 Tesouro Direto[7252:1554402] 16:    
2015-08-04 23:36:08.203 Tesouro Direto[7252:1554402] 17:    -0.25
2015-08-04 23:36:08.226 Tesouro Direto[7252:1554402] 18:    
2015-08-04 23:36:08.227 Tesouro Direto[7252:1554402] 19:    
2015-08-04 23:36:08.227 Tesouro Direto[7252:1554402] 20:    04/08/15   - 18:14
2015-08-04 23:36:08.227 Tesouro Direto[7252:1554402] 21: 

The log will show you witch number is each node.