简体   繁体   English

iPhone上带有NSXMLParser的核心数据错误地保存了对象

[英]Core Data with NSXMLParser on iPhone saving object incorrectly

I'm new to Objective-C, XCode and iPhone development in general and I'm having some issues with Core Data and NSXMLParser. 我一般不熟悉Objective-C,XCode和iPhone开发,但是Core Data和NSXMLParser存在一些问题。

Having followed Apples' tutorials SeismicXML (for NSXMLParser) and the Core Data on iPhone tutorial I've ran into an issue when assigning values to my Managed Object Models' entities properties. 在遵循Apple的SeismicXML教程(用于NSXMLParser)和iPhone上的Core Data教程之后,在为托管对象模型的实体属性分配值时遇到了一个问题。

To explain the situation my code only varies from the SeismicXML example by using CoreData to assign the currentParsedCharacterData to my managed objects rather than the standard NSObject which the SeismicXML project uses. 为了解释这种情况,我的代码仅与SeismicXML示例有所不同,使用CoreData将currentParsedCharacterData分配给我的托管对象,而不是SeismicXML项目使用的标准NSObject。

Below is the description output from my managed object. 以下是我的托管对象的描述输出。

county = "-53.25354768,4.256547";
friendly = "-53.25354768,4.256547";
image = nil;
latitude = -53.253547684;
link = "-53.25354768,4.256547";
longitude = nil;
name = "-53.25354768,4.256547";
postcode = "-53.25354768,4.256547";
shopDescription = nil;
shopID = 0;
tag = "-53.25354768,4.256547";
tags =     (
);
telephone = "-53.25354768,4.256547";
town = "-53.25354768,4.256547";

What appears to be happening is that all of the attributes/properties are assigned the value of the last node in my XML feed; 似乎正在发生的事情是,为所有属性/属性分配了XML提要中最后一个节点的值。 which happens to be longitude, latitude. 恰好是经度,纬度 Yet when logging parsed character data at the time of the property assignment it is the expected (and correct) value but when outputting the description of this object all string values are wrong and number values/otherwise are simply 0 or nil. 但是,在属性分配时记录解析的字符数据时,它是预期的(也是正确的)值,但是当输出该对象的描述时,所有字符串值都是错误的,而数字值/否则就是0或nil。

Any suggestions would be extremely appreciated. 任何建议将不胜感激。 If need be I can knock up a smaller project which shows this behaviour with the same XML feed that I am using. 如果需要的话,我可以使用与我使用的相同的XML提要创建一个较小的项目,以显示此行为。

EDIT: 编辑:

Here is an abbreviated example of what I am doing to get information into the managed object which results in the same error. 这是我为使信息进入托管对象而导致相同错误的工作的简化示例。

For convenience sake I have a zip of the project http://willb.ro/CoreDataProblemExample.zip 为了方便起见,我有一个项目的zip文件http://willb.ro/CoreDataProblemExample.zip

Debug Output 2009-11-16 14:31:20.357 ShittyExample[4360:4d07] Company Description: (entity: Company; id: 0x3f6e9e0 ; data: { companyDescription = "Top Shop are a leading brandname in the retail sec"; companyID = 66136112; name = "Top Shop are a leading brandname in the retail sec"; }) 调试输出2009-11-16 14:31:20.357 ShittyExample [4360:4d07]公司描述:(实体:公司; id:0x3f6e9e0;数据:{companyDescription =“顶级商店是零售业中领先的品牌名称”; companyID = 66136112; name =“顶级商店是零售业的领先品牌名称”;})

//XML
<channel>
    <company id="1">
        <name>Top Shop</name>
        <description>Top Shop are a leading brandname in the retail sector.</description>
    </company>
</channel>

// FeedImporter.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class RootViewController, Company;

@interface FeedImporter : NSObject {
    NSManagedObjectContext *managedObjectContext;
    RootViewController *rootViewController;
    NSMutableArray *companyList;

    // for downloading the xml data
    NSURLConnection *companyFeedConnection;
    NSMutableData *companyData;

    // these variables are used during parsing
    Company *currentCompanyObject;
    NSMutableArray *currentParseBatch;
    NSUInteger parsedCompaniesCounter;
    NSMutableString *currentParsedCharacterData;
    BOOL accumulatingParsedCharacterData;
    BOOL didAbortParsing;
}

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

@property (nonatomic, retain) RootViewController *rootViewController;
@property (nonatomic, retain) NSMutableArray *companyList;

@property (nonatomic, retain) NSURLConnection *companyFeedConnection;
@property (nonatomic, retain) NSMutableData *companyData;

@property (nonatomic, retain) Company *currentCompanyObject;
@property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
@property (nonatomic, retain) NSMutableArray *currentParseBatch;

- (void)parseFeed;
- (void)addCompaniesToList:(NSArray *)companies;
- (void)handleError:(NSError *)error;

@end

// FeedImporter.m    

#import "FeedImporter.h"
#import "RootViewController.h"
#import <CFNetwork/CFNetwork.h>
#import "Company.h"

@implementation FeedImporter

@synthesize managedObjectContext;
@synthesize rootViewController;

@synthesize companyList;
@synthesize companyFeedConnection;
@synthesize companyData;

@synthesize currentCompanyObject;
@synthesize currentParseBatch;
@synthesize currentParsedCharacterData;


- (void)dealloc {
    [super dealloc];
    [managedObjectContext release];
    [rootViewController release];
    [companyList release];
    [companyFeedConnection release];
    [companyData release];

    [currentCompanyObject release];
    [currentParseBatch release];
    [currentParsedCharacterData release];
}

- (id)init {
    if(self = [super init]) {
        // Custom loading logic goes here..
    }
    return self;
}

- (void)parseFeed {
    static NSString *feedURLString = @"http://willb.ro/companies.xml";
    NSURLRequest *companyURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURLString]];

    self.companyFeedConnection = [[[NSURLConnection alloc] initWithRequest:companyURLRequest delegate:self] autorelease];

    NSAssert(self.companyFeedConnection != nil, @"Failure to create URL connection.");

    // Start the status bar network activity indicator. We'll turn it off when the connection finishes or experiences an error.
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

#pragma mark NSURLConnection delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    self.companyData = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [companyData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;   
    if ([error code] == kCFURLErrorNotConnectedToInternet) {
        // if we can identify the error, we can present a more precise message to the user.
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"No Connection Error",                             @"Error message displayed when not connected to the Internet.") forKey:NSLocalizedDescriptionKey];
        NSError *noConnectionError = [NSError errorWithDomain:NSCocoaErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:userInfo];
        [self handleError:noConnectionError];
    } else {
        // otherwise handle the error generically
        [self handleError:error];
    }
    self.companyFeedConnection = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    self.companyFeedConnection = nil;
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;   

    [NSThread detachNewThreadSelector:@selector(parseCompanyData:) toTarget:self withObject:companyData];

    self.companyData = nil;
}

- (void)parseCompanyData:(NSData *)data {
    // You must create a autorelease pool for all secondary threads.
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    self.currentParseBatch = [NSMutableArray array];
    self.currentParsedCharacterData = [NSMutableString string];

    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
    [parser setDelegate:self];
    [parser parse];

    if ([self.currentParseBatch count] > 0) {
        [self performSelectorOnMainThread:@selector(addCompaniesToList:) withObject:self.currentParseBatch waitUntilDone:NO];
    }
    self.currentParseBatch = nil;
    self.currentCompanyObject = nil;
    self.currentParsedCharacterData = nil;

    // Save to our MOC...

    NSError *saveError;
    if(![self.managedObjectContext save:&saveError]) {
        // Handle MOM save error
        NSLog(@"error while saving shop to managed object model");

        NSError* error;
        if(![[self managedObjectContext] save:&error]) {
            NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
            NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
            if(detailedErrors != nil && [detailedErrors count] > 0) {
                for(NSError* detailedError in detailedErrors) {
                    NSLog(@"  DetailedError: %@", [detailedError userInfo]);
                }
            }
            else {
                NSLog(@"  %@", [error userInfo]);
            }
        }

    }
    else
    {
        NSLog(@"MOC saved sucessfully");

    }

    [parser release];        
    [pool release];
}

#pragma mark Parser constants

// Limit the number of parsed companies to 50.
static const const NSUInteger kMaximumNumberOfCompaniesToParse = 50;

static NSUInteger const kSizeOfCompanyBatch = 10;

static NSString * const kChannelElementName = @"channel";
static NSString * const kCompanyElementName = @"company";
static NSString * const kCompanyNameElementName = @"name";
static NSString * const kCompanyDescriptionElementName = @"description";

- (void)addCompaniesToList:(NSArray *)companies {
    [self.companyList addObjectsFromArray:companies];
    // The table needs to be reloaded to reflect the new content of the list.
    [rootViewController.tableView reloadData];
}

#pragma mark NSXMLParser delegate methods

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    if (parsedCompaniesCounter >= kMaximumNumberOfCompaniesToParse) {
        didAbortParsing = YES;
        [parser abortParsing];
    }
    if ([elementName isEqualToString:kCompanyElementName]) {
        Company *company = (Company *)[NSEntityDescription insertNewObjectForEntityForName:@"Company" inManagedObjectContext:self.managedObjectContext];
        self.currentCompanyObject = company;
        [company release];
        int companyIDInt = (int)[attributeDict valueForKey:@"id"];
        NSNumber *companyID = [NSNumber numberWithInt:companyIDInt];
        [self.currentCompanyObject setCompanyID:companyID];
    }
    else if ([elementName isEqualToString:kCompanyElementName] || [elementName isEqualToString:kCompanyNameElementName] || [elementName isEqualToString:kCompanyDescriptionElementName]) {
        accumulatingParsedCharacterData = YES;
        [currentParsedCharacterData setString:@""];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {     
    if ([elementName isEqualToString:kCompanyElementName]) {
        //NSLog(@"currentEarthquakeObject: %@", currentEarthquakeObject);
        [self.currentParseBatch addObject:self.currentCompanyObject];
        parsedCompaniesCounter++;
        if (parsedCompaniesCounter % kSizeOfCompanyBatch == 0) {
            [self performSelectorOnMainThread:@selector(addCompaniesToList:) withObject:self.currentParseBatch waitUntilDone:NO];
            self.currentParseBatch = [NSMutableArray array];
        }
        //NSLog(@"Reached end of company. Follows is a description of our company object: %@", [self.currentCompanyObject description]);
        NSLog(@"Company Description: %@", [self.currentCompanyObject description]);
    }
    else if ([elementName isEqualToString:kCompanyNameElementName]) {
        // Company Name
        [self.currentCompanyObject setName:self.currentParsedCharacterData];
        //NSLog(@"%@",self.currentParsedCharacterData);
    }
    else if ([elementName isEqualToString:kCompanyDescriptionElementName]) {
        // Company Description
        [self.currentCompanyObject setCompanyDescription:self.currentParsedCharacterData];
        //NSLog(@"%@",self.currentParsedCharacterData);
    }
    accumulatingParsedCharacterData = NO;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if (accumulatingParsedCharacterData) {
        [self.currentParsedCharacterData appendString:string];
    }
}


- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    if (didAbortParsing == NO) {
        [self performSelectorOnMainThread:@selector(handleError:) withObject:parseError waitUntilDone:NO];
    }
}

- (void)handleError:(NSError *)error {
    NSString *errorMessage = [error localizedDescription];
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error Title", @"Title for alert displayed when download or parse error occurs.") message:errorMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alertView show];
    [alertView release];
}

@end

It seems you are assigning where you ought to be copying. 似乎您正在分配应该复制的位置。 I'd have to see more of your code to be sure, but I'm almost certain a copy somewhere would solve your issues. 我必须确保能看到更多的代码,但是几乎可以肯定,某个地方的copy可以解决您的问题。

As stated above, i had the exact same problem. 如上所述,我有完全相同的问题。 But would now recommend to bypass the useless NSXMLParser (if using it WITH core data!!!) and using GDataXML instead. 但是现在建议绕过无用的NSXMLParser(如果将其与核心数据一起使用!!),而是改用GDataXML。 GDataXML is modelled on the NSXMLDocument class (which is a more elegant and workable solution to use with core data, only that you can't use NSXMLDocument with iOS). GDataXML是仿照NSXMLDocument类(这是一个更优雅的和可行的解决与核心数据的使用,只是你无法与iOS使用NSXMLDocument)。

See here for an excellent tutorial: http://www.raywenderlich.com/725/how-to-read-and-write-xml-documents-with-gdataxml :) 参见此处,获取出色的教程: http ://www.raywenderlich.com/725/how-to-read-and-write-xml-documents-with-gdataxml :)

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

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