iPhone上的NSXMLParser核心数据保存对象不正确

时间:2009-11-16 11:42:42

标签: iphone-sdk-3.0 core-data nsxmlparser

我是Objective-C,XCode和iPhone开发的新手,我在使用Core Data和NSXMLParser时遇到了一些问题。

在遵循Apples的教程SeismicXML(适用于NSXMLParser)和iPhone上的核心数据教程时,我在为我的托管对象模型的实体属性分配值时遇到了一个问题。

为了解释这种情况,我的代码与SeismicXML示例不同,它使用CoreData将currentParsedCharacterData分配给我的托管对象,而不是SeismicXML项目使用的标准NSObject。

以下是我的托管对象的描述输出。

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";

似乎正在发生的是,所有属性/属性都分配了XML Feed中最后一个节点的值;这恰好是经度,纬度。然而,当在属性赋值时记录解析的字符数据时,它是预期的(和正确的)值,但是在输出此对象的描述时,所有字符串值都是错误的,而数值/否则只是0或nil。

任何建议都将非常感谢。如果需要的话,我可以使用与我正在使用的XML Feed相同的方式来打开一个显示此行为的较小项目。

编辑:

以下是我为了将信息导入托管对象而导致相同错误的缩写示例。

为方便起见,我有一个项目http://willb.ro/CoreDataProblemExample.zip

的拉链
  

调试输出2009-11-16 14:31:20.357   ShittyExample [4360:4d07]公司   描述:   (entity:Company; id:0x3f6e9e0      ;数据:{       companyDescription =“Top Shop是零售业中的领先品牌   秒“;       companyID = 66136112;       name =“Top Shop是零售业中的领先品牌”; })

//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

2 个答案:

答案 0 :(得分:0)

您似乎正在指定应该复制的位置。我必须看到更多代码才能确定,但​​我几乎可以肯定copy某处可以解决您的问题。

答案 1 :(得分:0)

如上所述,我有完全相同的问题。但现在建议绕过无用的NSXMLParser(如果使用它与核心数据!!!)并使用GDataXML代替。 GDataXML是在NSXMLDocument类上建模的(这是一个更优雅,更实用的解决方案,可以与核心数据一起使用,只有你不能在iOS上使用NSXMLDocument)。

请在此处查看优秀教程: http://www.raywenderlich.com/725/how-to-read-and-write-xml-documents-with-gdataxml :)