libxml2 XML DOM анализ с XPathQuery

Опитвам се да анализирам следния XML файл:

<MesPar DH="HBCHa" StrNr="2416" Typ="10" Var="10">
    <Name>Aabach - Hitzkirch</Name>
    <Datum>11.11.2013</Datum>
    <Zeit>18:00</Zeit>
    <Wert>2.02</Wert>
    <Wert dt="-24h">1.93</Wert>
</MesPar>

<MesPar DH="HBCHa" StrNr="2312" Typ="02" Var="00">
    <Name>Aach - Salmsach</Name>
    <Datum>11.11.2013</Datum>
    <Zeit>18:00</Zeit>
    <Wert>406.47</Wert>
    <Wert dt="-24h">406.64</Wert>
</MesPar>

Ще прочета стойностите на елемента, ако атрибутът "StrNr" е равен на "2416". Моят код е:

NSURL *url = [NSURL URLWithString:@"http://www.hydrodaten.admin.ch/lhg/SMS.xml"];
NSData *webData = [NSData dataWithContentsOfURL:url options:NSUTF8StringEncoding error:nil];
TFHpple *parser = [TFHpple hppleWithData:webData isXML:YES];
NSString *xPathQuery = @"//AKT_Data/MesPar";
NSArray *arrayPaser= [parser searchWithXPathQuery:xPathQuery];

NSMutableArray *arrayName = [[NSMutableArray alloc] initWithCapacity:0];
NSMutableArray *arrayDatum = [[NSMutableArray alloc] initWithCapacity:0];
NSMutableArray *arrayWertDt24h = [[NSMutableArray alloc] initWithCapacity:0];


for (TFHppleElement *element in arrayPaser) {
    if ([[element firstChild] content]!=nil) {
        NSDictionary *attribute=[element attributes];

        NSString *string= [NSString stringWithFormat:@"%@",[attribute valueForKey:@"StrNr"]];

        if ([string isEqualToString:@"2416"]) {

            arrayName addObject:[element ??????];
            arrayDatum addObject:[element ?????];
            arrayWertDt24h addObject:[element ????];
        }

Не знам как да получа стойностите от елемента?


person user3022737    schedule 22.11.2013    source източник


Отговори (1)


Имам решение, което включва използването на вградения NSXMLParser и няколко метода NSXMLParserDelegate.

Нека първо да създадем подклас NSObject и да създадем клас анализатор. Ето .h:

#import <Foundation/Foundation.h>

@interface XMLParser : NSObject

- (id)initWithData:(NSData *)data;
- (BOOL)parse;

@end

Тук можете да видите, че ще захраним този обект с данните, които искате да анализирате, и след като това стане, можем да му кажем да анализира. Методът за анализ е просто обвивка около метода за анализ на NSXMLParser, който ще видите след малко.

Разширението на клас е мястото, където ще добавим частните свойства, които ще използваме за управление на данните, които анализираме. Изглежда, както следва:

@interface XMLParser ()
<NSXMLParserDelegate>

@property (nonatomic, strong) NSData *data;
@property (nonatomic, strong) NSXMLParser *parser;
@property (nonatomic, strong) NSMutableDictionary *objectDict;
@property (nonatomic, strong) NSMutableString *elementDataString;
@property (nonatomic, strong) NSMutableDictionary *wertTwo;
@property (nonatomic, assign, getter = isParsingWertTwo) BOOL parsingWertTwo;

@end

Свойствата data и parser се обясняват сами. Свойството objectDict е това, което ще използваме за съхраняване на данните, които искате да анализирате от този XML. ElementDataString ще съдържа символите, които анализаторът намира между етикетите на елемента. Имаме свойство wertTwo и флаг за указване, когато анализираме втория елемент Wert. Това е, за да можем да се уверим, че ще вземем атрибутите от този втори елемент Wert.

Началото на изпълнението изглежда както следва:

@implementation XMLParser

- (id)initWithData:(NSData *)data
{
    self = [super init];
    if (self) {
        self.data = data;
        self.parser = [[NSXMLParser alloc] initWithData:data];
        self.parser.delegate = self;
        self.objectDict = [@{} mutableCopy];
        self.wertTwo = [@{} mutableCopy];
    }
    return self;
}

- (BOOL)parse
{
    return [self.parser parse];
}

Както можете да видите от инициализатора, ние настройваме обектите, от които се нуждаем, заедно с данните и анализатора, за да извършим действителния анализ. Методът за анализ, както споменах, просто обвива метода за анализ на класа NSXMLParser. Всъщност връща BOOL и затова избрах да го върна и тук. Зададохме self като делегат на анализатора, така че трябва да приложим няколко от методите в протокола за делегат, за да получим необходимите данни. Делегираните методи се появяват, както следва:

#pragma mark - NSXMLParserDelegate

- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qName
    attributes:(NSDictionary *)attributeDict
{
    if ([elementName isEqualToString:@"MesPar"]) {

        // Get the value from the attribute dict of the MesPar element
        NSString *value = attributeDict[@"StrNr"];

        // Compare whether the value is equal to the desired value
        if ([value isEqualToString:@"2416"]) {

            // if the value is equal, add the attribute dict to the object dict
            [self.objectDict addEntriesFromDictionary:attributeDict];

            return;
        }
    }

    // If the element is Wert AND there is an attribute named dt we know this is the second Wert element
    if ([elementName isEqualToString:@"Wert"] && attributeDict[@"dt"]) {

        // add the attribute element to the wertTwo dict
        [self.wertTwo addEntriesFromDictionary:attributeDict];

        // Set the parsing flag to YES so we know where we are in the delegate methods
        self.parsingWertTwo = YES;

        return;
    }
}

- (void)parser:(NSXMLParser *)parser
 didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qName
{
    // if this is the Name element, set the element data in the object dict
    if ([elementName isEqualToString:@"Name"]) {
        [self.objectDict setObject:[self.elementDataString copy] forKey:@"name"];

        // set the data to nil since it will be reset by a delegate method for the next element
        self.elementDataString = nil;

        return;
    }

    if ([elementName isEqualToString:@"Datum"]) {
        [self.objectDict setObject:[self.elementDataString copy] forKey:@"datum"];

        // set the data to nil since it will be reset by a delegate method for the next element
        self.elementDataString = nil;

        return;
    }

    if ([elementName isEqualToString:@"Zeit"]) {
        [self.objectDict setObject:[self.elementDataString copy] forKey:@"zeit"];

        // set the data to nil since it will be reset by a delegate method for the next element
        self.elementDataString = nil;

        return;
    }


    if ([elementName isEqualToString:@"Wert"]) {

        // Checks to see if this is the Wert element AND that we are parsing the second element
        if (self.isParsingWertTwo) {
            [self.wertTwo setObject:[self.elementDataString copy] forKey:@"wertTwoString"];

            // set the wertTwo dict for the key wertTwo in the object dict
            // this allows us to pull out this info for the key wertTwo and includes the attribute of dt along with the elementDataString
            [self.objectDict setObject:[self.wertTwo copy] forKey:@"wertTwo"];

            // set the data to nil since it will be reset by a delegate method for the next element
            self.elementDataString = nil;

            return;
        }
        else{
            [self.objectDict setObject:[self.elementDataString copy] forKey:@"wertOne"];

            // set the data to nil since it will be reset by a delegate method for the next element
            self.elementDataString = nil;

            return;
        }
    }
}

- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    // You do not have to implement this but if you'd like here you can access `self.objectDict` which should have a representation of your XML you're looking to parse
}


- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // Append the foundCharacters (in between the element tags) to the data string
    [self.elementDataString appendString:string];
}

Кодът се коментира какво всъщност се случва, но накратко, анализаторът уведомява делегата, self в този случай, когато се случват определени неща, като например когато попадне на елемент или когато намира знаци. Едно нещо, което трябва да имате предвид е, че свойството elementDataString трябва да се зарежда лениво и ние го правим така:

// lazy loads the elementDataString if it is nil
// it will be set to nil after each time it is set in a dict
// this is why we copy it when we add it to the dict
- (NSMutableString *)elementDataString
{
    if (!_elementDataString) {
        _elementDataString = [NSMutableString string];
    }
    return _elementDataString;
}

Има няколко неща, които не съм разгледал, като например грешки при синтактичния анализ или допълнителни методи на делегиране, които може да ви интересуват. Това е едно конкретно решение, което използва вградени класове, вместо да разчита на библиотека на трета страна.

person Brian Palma    schedule 26.11.2013