核心数据:使用查找或插入/重复条目导入树结构

时间:2012-03-05 09:33:03

标签: ruby-on-rails ios core-data tree

我有一个来自rails应用程序的地方列表,我正试图在iOS5应用程序中导入。每个地方都有一个父母,这是一个地方本身。

我正在尝试使用字典

导入带有Core Data的JSON数据
- (void)initWithDictionary:(NSDictionary *)dictionary {
    self.placeId = [dictionary valueForKey:@"id"]; 
    id parent = [dictionary objectForKey:@"parent"];
    if (parent && parent != [NSNull null]) {
        NSDictionary *parentDictionary = parent;
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"placeId = %@", [parentDictionary objectForKey:@"id"]];
        NSArray *matching = fetchedWithPredicate(@"Place", self.managedObjectContext, predicate, nil);
        if ([matching count] > 0) {
            self.parent = [matching objectAtIndex:0];
        } else {
            self.parent = [NSEntityDescription insertNewObjectForEntityForName:@"Place" inManagedObjectContext:self.managedObjectContext];
            [self.parent initWithDictionary:parentDictionary];
        }
    }
}

fetchedWithPredicate是一个定义为

的方法
NSArray* fetchedWithPredicate(NSString *entityName, NSManagedObjectContext *context, NSPredicate *predicate, NSError **error) {
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setIncludesPendingChanges:YES];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
    [request setEntity:entity];
    [request setPredicate:predicate];
    NSArray *result = [context executeFetchRequest:request error:error];
    return result;
}

我在Place.m中也有一个验证方法,以确保我不创建与placeId相同的位置(placeId是服务器端的id)。

- (BOOL)validatePlaceId:(id *)value error:(NSError **)error {
    if (*value == nil)
        return YES;

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"placeId = %@ AND (NOT self IN %@)", *value, [NSArray arrayWithObject:self]];
    NSArray *matching = fetchedWithPredicate(@"Place", self.managedObjectContext, predicate, error);
    if ([matching count] > 0) {
        return NO;
    }
    else {
        return YES;
    }
}

要导入数据,我从服务器获取所有位置,以JSON格式返回。 每个Place都有自己的信息,以及一个包含父元素信息的子节点,这意味着多个子节点的每个父节点都会多次出现。它看起来像

{ "id": 73, 
  "name": "Some place", 
  "parent": { "id": 2,
              "name": "Parent's name"}
}

我认为上面的代码做了一些“查找或创建”,包含未保存的更改,但是没有问题。 但它仍然试图为某些地方创建多个条目(并且因为已经进行了验证而失败)。更深入地看,它确实为同一个placeId(不同的指针)插入了不同的核心数据对象,但我不知道为什么。

由于

2 个答案:

答案 0 :(得分:3)

听起来你已经有了一个唯一的id索引(很明显很好)。我认为您不是在将新插入的创建保存到核心数据之前,而是希望通过fetch返回它。简单(如果可能不太高性能,取决于有很多行)将在插入/插入每个对象后立即添加saveContext调用。

另一种方法是在两次传递中完成,首先完全在内存中创建一个单独的字典,其中键是id,对象是值。这样你就可以确保每个id只在那里一次。在他们全部进入该词典之后,您可以轻松地(或者更容易地)将它们全部添加到Core Data中。

答案 1 :(得分:0)

因此,经过一番调查后,这是因为我按名称对数据进行排序......

因此,如果一个地方A有5个孩子,其中3个人的名字在A的名字之前,那么代码就会:

  1. 创建那些父母本身没有父母的3个孩子(因为我的json没有返回关于父母父母的信息)
  2. 创建A
  3. 用A作为父母创建另外两个孩子(可能是因为它的排序方式,但这不会改变结论),所以父母有父母
  4. 现在我们有2个对象A,一个有父对象,另一个没有父对象,Core Data认为它有2个对象。

    简单的出路:我的树是嵌套的,所以我只需要按左边的值排序,这样我就会在孩子面前创建父母。

    “按名称排序”不是我的描述的一部分,因此我将scc的答案视为已接受:)