Jak używać NSXMLParser do parsowania elementów rodzic-dziecko o tej samej nazwie

Czy ktoś ma jakiś pomysł jak parsować poniższy xml używając modelu nsxmlparser class?

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

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

Chcę zebrać wszystkie trzy nazwy z tego pliku xml, czy to możliwe, Czy muszę zmienić na parsowanie oparte na drzewie?

Author: Adrian Kosmaczewski, 2010-01-05

3 answers

Jest to częsty problem z parserami takimi jak ten, typu sax, gdzie musisz ręcznie śledzić bieżącą głębokość drzewa XML, w którym się znajdujesz. Problem, jak zawsze, polega na tym, że załadowanie całego drzewa w strukturze DOM w pamięci może być niemożliwe, w zależności od wielkości danych, które chcesz manipulować.

Poniższy kod pokazuje klasę, która wykonuje to zadanie:

#import <Foundation/Foundation.h>

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

- (void)start;

@end

Oto implementacja:

#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

Jest to wynik uruchomienia wiersza poleceń narzędzie uruchamiające powyższą metodę "start":

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
 32
Author: Adrian Kosmaczewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-05 11:20:57
- (NSInteger)columnNumber

Daje poziom zagnieżdżania NSXMLParser

 6
Author: yasirmturk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-15 11:00:27

Bardzo łatwiejszy sposób rozwiązania niż przyjęta odpowiedź, dla krótkich plików XML:

Przykład XML:

<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>

Zaimplementuj licznik:

@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;

}
Skąd będę wiedział, że numer czarownicy to węzeł czarownic ? Widząc dziennik. Będzie to coś w stylu:
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: 

Dziennik pokaże Ci numer czarownicy to każdy węzeł.

 -1
Author: brbgyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-05 02:47:28