将XML已解析数据保存到NSMutableDictionary最佳实践

时间:2013-08-11 10:06:05

标签: iphone ios objective-c xml nsxmlparser

这个问题可能经常被问到,但我已经阅读了几乎所有这些问题,但却无法找到解决方案。 我计划将解析的数据保存到NSMutableDictionary。解析器工作正常,如果我得到一个日志,它会显示解析的所有数据。问题是最后一项仅保存在NSDictionary中。我可以猜测我把字典放错了方法,但我找不到更好的保存解决方案。我使用字典来保存元素的名称和文本。这是我的代码:

@implementation Parser
@synthesize currentElementPointer, rootElement;

@synthesize dictionary;

-(id)initParser
{
    if(self = [super init]) {
        tvc = (TimeTableViewController*)[[UIApplication sharedApplication]delegate];

        APUAppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
        context = [appDelegate managedObjectContext];

        [self deleteAllObjects:@"TimeTable"];
    }
    return self;
}


#pragma mark -
#pragma mark PARSER

-(void)parserDidStartDocument:(NSXMLParser *)parser
{
    self.parsing = YES;
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if([elementName isEqualToString:@"intake"])
    {
        NSString *name = attributeDict[@"name"];
        self.parsing = [name isEqualToString:[self sendIntakeToParser]];
    }
    if(![self isParsing]) return;

    if(self.rootElement == nil) {
        self.rootElement = [[List alloc]init];
        self.currentElementPointer = self.rootElement;
    } else {
        List *newList = [[List alloc]init];
        newList.parent = self.currentElementPointer;
        [self.currentElementPointer.subElements addObject:newList];
        self.currentElementPointer = newList;
    }

    self.currentElementPointer.name = elementName;
    self.currentElementPointer.attributes = attributeDict;

}

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

    if(![self isParsing]) return;

    if([self.currentElementPointer.text length] > 0) {
        self.currentElementPointer.text = [self.currentElementPointer.text stringByAppendingString:string];
    } else {
        self.currentElementPointer.text = string;
    }


    dictionary = [[NSMutableDictionary alloc]init];
    [dictionary setObject:[self.currentElementPointer text] forKey:[self.currentElementPointer name]];

}


-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if([self isParsing])
    {
        self.currentElementPointer = self.currentElementPointer.parent;
    } else if ([elementName isEqualToString:@"intake"])
    {
        self.parsing = YES;
    }
}

这是我的xml结构: enter image description here

1 个答案:

答案 0 :(得分:1)

问题的根源在于,每次调用parser:foundCharacters:时都会重新实例化字典,每个有文本的元素都会调用至少一次。您可以做的一件事是在parser:didStartElement:...等于elementName时将字典实例化移至@"timetable",在parser:didStartDocument:中实例化可变数组,然后在parser:didEndElement:...中实例化,如果elementName是“@”时间表“,请将其添加到您的数组中。

这方面的一个例子如下所示:

Parser.h:

#import <Foundation/Foundation.h>

@interface Parser : NSObject <NSXMLParserDelegate>

- (void)parseData:(NSData *)data;

@end

Parser.m:

#import "Parser.h"

@interface Parser ()

@property (nonatomic,strong) NSMutableArray *timetableDictionaries;
@property (nonatomic,strong) NSMutableDictionary *currentTimetableDictionary;
@property (nonatomic,copy) NSString *currentElementName;

@end

@implementation Parser

- (void)parseData:(NSData *)data
{
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
    parser.delegate = self;
    [parser parse];
}


- (void)parserDidStartDocument:(NSXMLParser *)parser
{
    self.timetableDictionaries = [NSMutableArray array];
}


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    self.currentElementName = elementName;
    if ([elementName isEqualToString:@"timetable"]) {
        self.currentTimetableDictionary = [NSMutableDictionary dictionary];
    }
}


- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if (self.currentElementName) {
        NSString *existingCharacters = self.currentTimetableDictionary[self.currentElementName] ? : @"";
        NSString *nextCharacters = [existingCharacters stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
        if ([nextCharacters length] > 0) {
            self.currentTimetableDictionary[self.currentElementName] = nextCharacters;
        }
    }
}


- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementName isEqualToString:@"timetable"]) {
        [self.timetableDictionaries addObject:self.currentTimetableDictionary];
        self.currentTimetableDictionary = nil;
    }
    self.currentElementName = nil;
}


- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    NSLog(@"timetable dictionaries = %@",self.timetableDictionaries);
}

@end

我用以下文件测试了上面的类:

<weekof week="2013-07-29">
    <intake name="APCF1304">
        <timetable>
            <date>SOME DATE</date>
            <time>SOME TIME</time>
            <lecturer>SOME LECTURER</lecturer>
        </timetable>
        <timetable>
            <date>ANOTHER DATE</date>
            <time>ANOTHER TIME</time>
            <lecturer>ANOTHER LECTURER</lecturer>
        </timetable>
    </intake>
</weekof>

它提供了以下输出:

2013-08-11 11:50:03.205 MyParser[25368:c07] timetable dictionaries = (
        {
        date = "SOME DATE";
        lecturer = "SOME LECTURER";
        time = "SOME TIME";
    },
        {
        date = "ANOTHER DATE";
        lecturer = "ANOTHER LECTURER";
        time = "ANOTHER TIME";
    }
)

但是,你已经构建了一个文档树,大概是为了另一个目的,所以你可以在解析结束时重新使用它来构建你的词典。例如,如果你只想要每个时间表的字典,你可以用伪代码做一些看起来像这样的事情:

dictionaries = [new mutable array]
element = self.root
[extract dictionaries from: element to:dictionaries]

implementation of extractDictionariesFrom:element to:dictionaries:
    if any subelement has non-nil text:
        item = [new mutable dictionary]
        for each sub element:
            item[element name] = element text
        [add item to dictionaries]
    else
        for each subelement:
            [extract dictionaries from: subelement to: dictionaries]

然后你可以放弃在解析器回调中使用可变字典。