이 질문은 자주 묻는 질문 일지 모르지만 거의 모든 질문을 읽었지만 내 경우에 대한 해결책을 찾지 못했습니다. 파싱 된 데이터를 NSMutableDictionary에 저장할 계획입니다. 파서가 정상적으로 작동합니다. 로그를 얻으면 모든 데이터를 분석합니다. 문제는 마지막 항목이 NSDictionary에만 저장된다는 것입니다. 내가 잘못한 방법으로 사전을 배치했다는 것을 짐작할 수 있지만 저장을위한 더 나은 솔루션을 찾을 수 없습니다. 나는 요소의 이름과 텍스트를 보유하기 위해 사전을 사용합니다. XML 구문 분석 된 데이터를 NSMutableDictionary에 저장 우수 사례
답변
문제의 원인은 당신이 적어도 한 번 당 호출됩니다 parser:foundCharacters:
각 호출하여 사전을 다시 인스턴스화이다 : 이것은 내 XML 구조가
@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;
}
}
입니다 : 여기 내 코드입니다 텍스트가있는 요소. 당신이 할 수있는 한 가지는 과 같을 때마다 parser:didStartElement:...
으로 사전 인스턴스화를 이동하고, parser:didStartDocument:
에 가변 배열을 인스턴스화 한 다음 parser:didEndElement:...
에 요소 이름이`@ "시간표"인 경우 배열에 추가 할 수 있습니다.
이것의 예는 다음과 같다 :
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]
그런 다음 당신은 당신의 파서 콜백에서 변경 가능한 사전의 사용을 폐기 할 수있다.
친애하는 Carl, 문제의 원인을 알고 있습니까? 마지막 요소가 저장하는 이유는 무엇입니까? 나는 또한 NSArray (addObject)로 시도했지만 같은 문제가 발생했습니다. – Ben
@Ben 방금 다른 해결책을 포함하도록 답변을 업데이트했습니다. –
파서 초기화 메소드에서 사전의 인스턴스를 옮겼습니다. 마지막 요소 대신 마지막 레코드를 저장합니다. 예를 들어 지난 번에 마지막 요소 인
모범 사례는 JSON을 사용하는 것입니다. – iiFreeman
구현할 웹 서비스는 XML 파일 만 제공합니다. – Ben