У меня есть решение, которое включает использование встроенного 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