简体   繁体   English

使用类别将XML解析为NSManagedObjects,以及如何处理类别中的属性?

[英]Parsing XML into NSManagedObjects using categories and what to do with properties in categories?

Based on the excellent example "Parsing XML with NSXMLParser" in the book "The Big Nerd Ranch Guide" (3rd ed.), I haved added categories to my NSManagedObjects for which I want to add XML parsing. 基于《 The Big Nerd Ranch Guide》(第3版)一书中的出色示例“使用NSXMLParser解析XML”,我已经为要添加XML解析的NSManagedObjects添加了类别。 These categories provide only parsing functionality. 这些类别仅提供解析功能。

This is how I have implemented these categories: .h: 这就是我实现以下类别的方式:.h:

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

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

@end

.m: .m:

@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;
}

As you can see in this code snippet, I switch the parser delegate to other subclasses / XML child elements to have them further process the next XML elements, which belong to them until the end of the XML element is reached and the delegate is set back to the parent. 如您在此代码片段中所看到的,我将解析器委托切换到其他子类/ XML子元素,以使它们进一步处理下一个属于它们的XML元素,直到到达XML元素的末尾并放回委托为止。给父母

This is why I need to store the parent delegate in the child. 这就是为什么我需要在孩子中存储父代表。 However, ivars and properties are not allowed in categories. 但是,不允许在类别中使用ivars和属性。

I came up with this solution which seems to circumvent this problem: 我想出了这个解决方案,似乎可以解决这个问题:

Child element, h: 子元素,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

I save the delegate to the parent in a variable _parentDelegate which is declared outside the ivar declaration block and does not seem to be a real ivar. 我将委托保存到父目录中的变量_parentDelegate ,该变量在ivar声明块之外声明,似乎不是真正的ivar。

This code works well in my tests and I wonder if I missed something which will turn out to become a problem later in the development process or if this design is ok. 这段代码在我的测试中效果很好,我想知道我是否错过了一些会在开发过程中成为问题的东西,或者这种设计是否可行。

What are your thoughts on that? 您对此有何看法?

Thank you! 谢谢!

I'm not sure how the compiler will treat that variable. 我不确定编译器将如何处理该变量。 Could it be allocated so that only one variable is shared by all objects of this type? 可以分配它,以便所有此类对象共享一个变量吗? If your XML is parsed such that more than one IBCompany exists at a point in time it could cause a problem. 如果对您的XML进行了分析,使得某个时间点存在多个IBCompany,则可能会引起问题。 I'd write a test that allocated two IBCompany objects, cause them both to write different values to _parentDelegate, then assert the values are different. 我要编写一个分配了两个IBCompany对象的测试,使它们都向_parentDelegate写入不同的值,然后断言这些值是不同的。

Or ignore the issue if there is no possibility that two IBCompany objects are parsed in parallel. 如果无法并行解析两个IBCompany对象,则忽略该问题。 You'd have to ensure that the XML can't have an IBCompany inside another IBCompany, multiple parts of the XML will not be processed in parallel, and that multiple XML documents will not be processed in parallel. 您必须确保XML不能在另一个IBCompany中包含一个IBCompany,不会并行处理XML的多个部分,并且不会并行处理多个XML文档。

I don't see the need for a category. 我认为不需要类别。 Categories are useful when you shouldn't write a subclass to an existing class, such as adding functionality to classes in the Cocoa framework. 当您不应该向现有类编写子类时,例如在Cocoa框架中为类添加功能时,类别非常有用。 You are writing a custom subclass, so why not add the ivar to your subclass? 您正在编写一个自定义子类,那么为什么不将ivar添加到您的子类中呢? You can have additional ivars in managed objects that are not saved in the Core Data backing stores. 您可以在托管对象中拥有其他ivars,这些ivars没有保存在Core Data后备存储中。 At most I'd just use an extension to segregate XML parsing code from the rest of the managed object. 最多我只是使用扩展将XML解析代码与其余托管对象隔离开来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM