Отображение отношений в RestKit через массив идентификаторов не работает

Я пытаюсь сопоставить пользователей и группы с объектами CoreData через RestKit, поддерживая отношения между ними.

JSON для пользователей это что-то вроде

{"users":
  [{"fullname": "John Doe", "_id": "1"}
   ...
  ]
}

и JSON для групп это что-то вроде

{"groups":
  [{"name": "John's group", "_id": "a",
    "members": ["1", "2"]
   ...
  ]
}

У меня также есть соответствующие объекты Core Data,User а такжеGroupс сопоставлением отношений один-ко-многим между группами и пользователями. Свойство отношения вGroup называетсяmembers и отдельныйNSArray называетсяmemberIDs содержит массив идентификаторов строк всех пользователей.

Что я хочу сделать в RestKit - это загрузить эти объекты и отобразить для меня отношения. Код для загрузки пользователей - простой стандартный материал, а код для загрузки групп - что-то вроде

RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];

// Create an empty user mapping to handle the relationship mapping
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class] inManagedObjectStore:objectManager.objectStore];

[groupMapping hasMany:@"members" withMapping:userMapping];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];

RKObjectRouter* router = objectManager.router;

[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];

Когда я запускаю этот код, я получаю следующее предупреждение несколько раз (кажется, примерно в два раза за отношения):

2012-10-24 08:55:10.170 Test[35023:18503] W restkit.core_data.cache:RKEntityByAttributeCache.m:205 Unable to add object with nil value for attribute 'identifier': <User: 0x86f7fc0> (entity: User; id: 0x8652400 <x-coredata:///User/t01A9EDBD-A523-4806-AFC2-9B06873D764E531> ; data: {
    fullname = nil;
    identifier = nil;
})

Странно то, что отношения устанавливаются должным образом (даже если посмотреть на базу данных sqlite, и там все выглядит хорошо), но я не доволен тем, что что-то явно идет не так в коде RestKit, и, похоже, это как-то связано с фиктивное отображение пользователя, которое я создаю в приведенном выше коде (оно пустое, поскольку в массиве идентификаторов нечего сопоставлять).

Я пробовал альтернативы, например, когда я добавляю отображение пути ключа:

[userMapping mapKeyPath:@"" toAttribute:@"identifier"];

Жалуется, очевидно, потому что ключевой путь пуст

2012-10-24 09:24:11.735 Test[35399:14003] !! Uncaught exception !!
[<__NSCFString 0xa57f460> valueForUndefinedKey:]: this class is not key value coding-compliant for the key .

И если я попытаюсь использовать реальныйUser отображение

[groupMapping hasMany:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"]];

Я получаю следующую ошибку

2012-10-24 09:52:49.973 Test[35799:18403] !! Uncaught exception !!
[<__NSCFString 0xa56e9a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.
2012-10-24 09:52:49.973 Test[35799:18403] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0xa56e9a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.'

Похоже, RestKit пытается сопоставить сами строки идентификатора в массиве memberIDsUser объекты, которые не имеют никакого смысла. Я видел примеры, где массив отношений представляет собой список словарей с ключами (например, называетсяid) со ссылкой на другой объект, например:

{"groups":
  [{"name": "John's group", "_id": "a",
    "members": [
      {"_id": "1"},
      {"_id": "2"}
     ]
   ...
  ]
}

Но я не хочу менять свой JSON таким образом (кроме того, я надеюсь, что RestKit достаточно гибок для поддержки другого способа).

Кто-нибудь знает, каков правильный способ создания такого рода отношений в RestKit?

Обновить

В итоге я изменил интерфейс REST для отправки списка словарей, содержащих идентификаторы объектов пользователя (как в последнем примере выше), и заставил его работать. Все еще не совсем доволен настройкой, но код теперь выглядит так

RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members._id" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];

// Create an empty user mapping to handle the relationship mapping
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class] inManagedObjectStore:objectManager.objectStore];
userMapping.primaryKeyAttribute = @"identifier";
[userMapping mapKeyPath:@"_id" toAttribute:@"identifier"];

[groupMapping hasMany:@"members" withMapping:userMapping];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];

RKObjectRouter* router = objectManager.router;

[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];

На всякий случай это поможет кому-то еще в подобной ситуации. Могут все еще быть некоторые проблемы с подходом, который я выбрал, но пока все хорошо (он даже обрабатывает загрузку вне порядка, что приятно) - все равно хотел бы услышать от кого-нибудь, если есть ответ на мой оригинальный вопрос.

Ответы на вопрос(1)

Решение Вопроса

а также понял, что обновленный подход в моем вопросе должен использоваться только для вложенных объектов, а не ссылочных объектов. Эта тема вывела меня на правильный путь:https://groups.google.com/forum/#!msg/restkit/swB1Akv2lTE/mnP2OMSqElwJ (стоит прочитать, если вы имеете дело с отношениями в RestKit)

Предположим, что группы JSON по-прежнему выглядят как исходные JSON:

{"groups":
  [{"name": "John's group", "_id": "a",
    "members": ["1", "2"]
   ...
  ]
}

Также предположим, что пользователи сопоставлены с ключомusers (то есть как в чем-то вроде[objectManager.mappingProvider setMapping:userMapping forKeyPath:@"users"];) правильный способ отображения отношений выглядит следующим образом:

RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];

[groupMapping mapKeyPath:@"users" toRelationship:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"] serialize:NO];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];

RKObjectRouter* router = objectManager.router;
[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];

Когда я попробовал этот подход ранее, меня смутила эта строка

[groupMapping mapKeyPath:@"users" toRelationship:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"] serialize:NO];

где первый путь ключа ссылается на отображение пути ключа для объектов, на которые ссылаются (users в данном случае), а не ключевой путь отношений внутриgroup отображаемый объект (который был быmembers в этом случае).

С этим изменением все отношения работают как положено, и я счастлив.

 vitaminwater11 сент. 2013 г., 11:18
@abisson проверить этот ответ от Блейк Уоттерс:groups.google.com/d/msg/restkit/9MG0CPi1aS0/olQGaSiJQjAJ
 abisson29 авг. 2013 г., 22:27
Привет! Я пытаюсь сделать то же самое, но мне было интересно, какой тип memberIDS в CoreData? Я постараюсь как можно скорее! Спасибо!

Ваш ответ на вопрос