1

基于“The Big Nerd Ranch Guide”(第 3 版)一书中的优秀示例“使用 NSXMLParser 解析 XML”,我已经添加了NSManagedObjects要为其添加 XML 解析的类别。这些类别仅提供解析功能。

这就是我实现这些类别的方式:.h:

#import "IBCompany.h"
@interface IBCompany (Xml) <NSXMLParserDelegate>

- (void)parseXmlString:(NSString*)xmlStr withCompletion:(void(^)(NSError *error))completionBlock;

@end

米:

@implementation IBCompany (Xml) 

- (void)parseXmlString:(NSString*)xmlStr withCompletion:(void(^)(NSError *error))completionBlock;
{
    NSData *xmlData = [xmlStr dataUsingEncoding:NSUTF8StringEncoding];
    NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
    parser.delegate = self;

    [parser parse];

    xmlData = nil;

    NSError *error;
    completionBlock(error);
    }


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"Issue"]) {
                IBIssue *issue = [NSEntityDescription insertNewObjectForEntityForName:@"IBIssue" inManagedObjectContext:self.managedObjectContext];
                issue.company = self;          
                issue.parentParserDelegate = self;
                parser.delegate = issue;
}

正如您在此代码片段中看到的,我将解析器委托切换到其他子类/XML 子元素,以让它们进一步处理属于它们的下一个 XML 元素,直到到达 XML 元素的末尾并设置委托给父母。

这就是为什么我需要将父委托存储在孩子中。但是,类别中不允许使用 ivars 和属性。

我想出了这个似乎可以绕过这个问题的解决方案:

子元素,h:

#import "IBIssue.h"

@interface IBIssue (Xml) <NSXMLParserDelegate>
@property id parentParserDelegate;
@end


#import "IBIssue+Xml.h"

@implementation IBIssue (Xml)

NSMutableString *currentString;
NSString *currentXmlDocument;

id _parentParserDelegate;

- (id)parentParserDelegate
{
    return _parentParserDelegate;
}

- (void)setParentParserDelegate:(id)parentParserDelegate;
{
    _parentParserDelegate = parentParserDelegate;
}

- (NSDateFormatter*)dateFormatter
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
    [dateFormatter setDateFormat:@"yyy-MM-dd"];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT: 0]];
    return dateFormatter;
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ([elementName isEqualToString:@"IssueID"]) {
        currentString = [[NSMutableString alloc]init];

        if      ([attributeDict[@"Type"] isEqualToString:@"Ticker"])        self.ticker = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"Name"])          self.issueName = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"CUSIP"])         self.cusip = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"ISIN"])          self.isin = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"RIC"])           self.ric = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"SEDOL"])         self.sedol = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"DisplayRIC"])    self.displayRic = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"InstrumentPI"]) ; //
        else if ([attributeDict[@"Type"] isEqualToString:@"QuotePI"])      ; //

    } else if ([elementName isEqualToString:@"Exchange"]) {
        currentString = [[NSMutableString alloc]init];

        self.exchangeCode = attributeDict[@"Code"];
        self.exchangeCountry = attributeDict[@"Country"];
        self.exchange = currentString;
    } else if ([elementName isEqualToString:@"MostRecentSplit"]) {
        currentString = [[NSMutableString alloc]init];

        self.mostRecentSplitDate = [self.dateFormatter dateFromString:attributeDict[@"Date"]];
        // self.mostRecentSplit = [NSNumber numberWithFloat: currentString.floatValue];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // NSLog(@"appendString: %@", string);
    [currentString appendString:string];
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementName isEqualToString:@"Issue"]) {
        parser.delegate = self.parentParserDelegate;
    } else if ([elementName isEqualToString:@"MostRecentSplit"]) {
        self.mostRecentSplit = [NSNumber numberWithFloat: currentString.floatValue];
    }

    currentString = nil;
}

@end

我将委托保存在一个变量中,该变量_parentDelegate在 ivar 声明块之外声明,并且似乎不是真正的 ivar。

这段代码在我的测试中运行良好,我想知道我是否遗漏了一些在开发过程后期会成为问题的东西,或者这个设计是否可以。

你对此有何看法?

谢谢!

4

1 回答 1

0

我不确定编译器将如何处理该变量。是否可以分配它以使该类型的所有对象仅共享一个变量?如果您的 XML 被解析为在某个时间点存在多个 IBCompany,则可能会导致问题。我会编写一个分配两个 IBCompany 对象的测试,使它们都向 _parentDelegate 写入不同的值,然后断言这些值不同。

如果不可能并行解析两个 IBCompany 对象,则忽略此问题。您必须确保 XML 不能在另一个 IBCompany 中包含 IBCompany,不会并行处理 XML 的多个部分,并且不会并行处理多个 XML 文档。

我不认为需要一个类别。当您不应该将子类写入现有类时,类别很有用,例如向 Cocoa 框架中的类添加功能。您正在编写一个自定义子类,那么为什么不将 ivar 添加到您的子类中呢?您可以在未保存在 Core Data 后备存储中的托管对象中拥有其他 ivars。最多我只是使用扩展将 XML 解析代码与其他托管对象隔离开来。

于 2012-10-29T14:57:20.493 回答