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 и создадим класс парсера. Вот .ч:

#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