Restkit: Как настроить сопоставления для ПУБЛИКАЦИИ вложенного массива NSManagedObjects

#ios #arrays #restkit #one-to-many #restkit-0.20

#iOS #массивы #restkit #один ко многим #restkit-0.20

Вопрос:

Пожалуйста, пропустите эту часть до ОБНОВЛЕНИЯ 3.

Среда: iOS7.1, MagicalRecord, Restkit, Mogenerator

Требуемая форма JSON выглядит следующим образом:

 {"amount":[10,20,50]}  (set of TSNDecimal's in TSNTagAmountRequest)
  

В запросе участвуют следующие объекты:

 @interface TSNTagAmountRequest: NSManagedObject
@property (nonatomic, strong) NSSet *amount;
@end
  

где набор значений задается из значений TSNDecimal:

 @interface TSNDecimal: NSManagedObject
@property (nonatomic, strong) NSDecimalNumber* number;
@end
  

Десятичное число с номером имени является вспомогательным.

RKObjectMapping для TSNDecimal:

 RKEntityMapping *decimalMapping = [RKEntityMapping 
                                       mappingForEntityForName:@"TSNDecimal" 
                                          inManagedObjectStore:managedObjectStore];

// map the value in JSON directly to the helper "number"
[decimalMapping addPropertyMapping:
    [RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"number"]];
  

Предыдущая строка — это код, который я ожидал бы десериализовать правильно «amount»: [10,20,50] до трех десятичных чисел, соответственно их числовым атрибутам (это работает), а также он должен правильно сериализоваться из набора десятичных чисел (через число) в JSON: «amount»: [10,20 ..] Это работает при анализе ответа, но не для запроса.

Остальное — это сопоставления отношений:

 RKPropertyMapping* decimalPropertyMappingAmount =
    [RKRelationshipMapping relationshipMappingFromKeyPath:@"amount"
                                                toKeyPath:@"amount"
                                              withMapping:decimalMapping];
  

(Я также пробовал FromKeyPath:@»amount.number» toKeyPath:@»amount», но возникает исключение из-за передачи неправильного пути в KVC ([self.object valueForKey:key]))

Наконец, я определил взаимосвязь между TSNTagAmountRequest и TSNDecimal: tagAmountRequestMapping = [RKObjectMapping RequestMapping]; [tagAmountRequestMapping addPropertyMapping:decimalPropertyMappingAmount];

и добавьте дескриптор в менеджер RK:

 requestDescriptor = [RKRequestDescriptor 
            requestDescriptorWithMapping:tagAmountRequestMapping
                             objectClass:[TSNTagAmountRequest class]
                             rootKeyPath:nil
                                  method:RKRequestMethodPOST];

[_rkManager addRequestDescriptor:requestDescriptor];
  

Проблема в том, что в NSJSONSerialization dataWithJSONObject возникает исключение:optionserror: очевидно, потому, что TSNDecimal не преобразуется из TSNDecimal.number в NSDecimalNumber, который понимает NSJSONSerialization.

Исключение: недопустимый тип в JSON write (TSNDecimal), поскольку данные, поступающие в dataFromObject в RKNSJSONSerialization, содержат объекты TSNDecimal, созданные RKObjectMappingOperationDataSource (не RKManagedObjectMappingOperationDataSource)

 {
    amount =     (
        "<TSNDecimal: 0xc1d0bf0> (entity: (null); id: (null) ; data: {n})",
        "<TSNDecimal: 0x11543080> (entity: (null); id: (null) ; data: {n})"
    );
}
  

it should be NSDecimalNumber instead of TSNDecimal I guess.

In summary I was expecting the code

 [decimalMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"number"]];
  

выполняет свою работу и переводит «число» обратно в nil (то есть в неназванный элемент в коллекции), но так не работает. В разделе Сопоставление значений без путей к ключам есть этот пример, но я смущен использованием [RKResponseDescriptor responseDescriptorWithMapping: если я хочу составить тело JSON. Синтаксический анализ из ответа хорошо работает с

 [decimalMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"number"]];
RKPropertyMapping* decimalPropertyMappingAmount =
    [RKRelationshipMapping relationshipMappingFromKeyPath:@"amount"
                                                toKeyPath:@"amount"
                                              withMapping:decimalMapping];
  

почему не для запроса?

Проблема, которую я вижу, заключается в CoreData: ошибка: не удалось вызвать назначенный инициализатор в классе NSManagedObject ‘TSNDecimal’

Основные объекты данных инициализируются с помощью

 TSNTagAmountRequest* request = [TSNTagAmountRequest requestWithModel:self];    


TSNDecimal* decimalEntity = nil;

for(NSDecimalNumber* number in amount) {

    decimalEntity = [TSNDecimal MR_createEntity];
    decimalEntity.number = number;
    [request addAmountObject:decimalEntity];
}
  

Вот журнал:

     D restkit.object_mapping:RKMappingOperation.m:860 Starting mapping operation...
    T restkit.object_mapping:RKMappingOperation.m:861 Performing mapping operation: <RKMappingOperation 0x9681ca0> for '__NSDictionaryM' object. Mapping values from object <TSNTagAmountRequest: 0xf7b8c90> (entity: TSNTagAmountRequest; id: 0xf784700 <x-coredata:///TSNTagAmountRequest/tB2DF8887-7E6C-420F-998A-D250751E5CED164> ; data: {
                                                                                                                                                                                                              amount =     (
                                                                                                                                                                                                                            "0x9664220 <x-coredata:///TSNDecimal/tB2DF8887-7E6C-420F-998A-D250751E5CED165>",
                                                                                                                                                                                                                            "0x9616390 <x-coredata:///TSNDecimal/tB2DF8887-7E6C-420F-998A-D250751E5CED166>"
                                                                                                                                                                                                                            );
                                                                                                                                                                                                              }) ((null)) to object {
    } with object mapping (null)
    D restkit.object_mapping:RKMappingOperation.m:641 Mapping one to many relationship value at keyPath 'amount' to 'amount'
    CoreData: error: Failed to call designated initializer on NSManagedObject class 'TSNDecimal'
    T restkit.object_mapping:RKMappingOperation.m:542 Performing nested object mapping using mapping <RKRelationshipMapping: 0x95a0df0 amount => amount> for data: <TSNDecimal: 0x9652900> (entity: TSNDecimal; id: 0x9664220 <x-coredata:///TSNDecimal/tB2DF8887-7E6C-420F-998A-D250751E5CED165> ; data: {
                                                                                                                                                                                            number = 5;
                                                                                                                                                                                            })
    D restkit.object_mapping:RKMappingOperation.m:860 Starting mapping operation...
    T restkit.object_mapping:RKMappingOperation.m:861 Performing mapping operation: <RKMappingOperation 0x96b3d60> for 'TSNDecimal' object. Mapping values from object <TSNDecimal: 0x9652900> (entity: TSNDecimal; id: 0x9664220 <x-coredata:///TSNDecimal/tB2DF8887-7E6C-420F-998A-D250751E5CED165> ; data: {
                                                                                                                                                                                                number = 5;
                                                                                                                                                                                                }) ({
        mapping =     {
            collectionIndex = 3221208359;
        };
    }) to object <TSNDecimal: 0x96b2880> (entity: (null); id: (null) ; data: {
    }) with object mapping (null)
    T restkit.object_mapping:RKMappingOperation.m:439 Found transformable value at keyPath '(null)'. Transforming from class 'TSNDecimal' to 'NSDecimalNumber'
    E restkit.object_mapping:RKMappingOperation.m:441 Failed transformation of value at keyPath '(null)' to representation of type 'NSDecimalNumber': Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3002 "Failed transformation of value '<TSNDecimal: 0x9652900> (entity: TSNDecimal; id: 0x9664220 <x-coredata:///TSNDecimal/tB2DF8887-7E6C-420F-998A-D250751E5CED165> ; data: {
    number = 5;
    }) ({
        mapping =     {
            collectionIndex = 3221208359;
        };
    })' to NSDecimalNumber: none of the 2 value transformers consulted were successful." UserInfo=0x96bd900 {detailedErrors=(
    "Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3002 "The given value is not already an instance of 'NSDecimalNumber'" UserInfo=0x96bd630 {NSLocalizedDescription=The given value is not already an instance of 'NSDecimalNumber'}",
    "Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3000 "Expected an `inputValue` of type `NSNull`, but got a `TSNDecimal`." UserInfo=0x96bd720 {NSLocalizedDescription=Expected an `inputValue` of type `NSNull`, but got a `TSNDecimal`.}"
    ), NSLocalizedDescription=Failed transformation of value '<TSNDecimal: 0x9652900> (entity: TSNDecimal; id: 0x9664220 <x-coredata:///TSNDecimal/tB2DF8887-7E6C-420F-998A-D250751E5CED165> ; data: {
    number = 5;
    }) ({
        mapping =     {
            collectionIndex = 3221208359;
        };
    })' to NSDecimalNumber: none of the 2 value transformers consulted were successful.}
    T restkit.object_mapping:RKMappingOperation.m:519 Did not find mappable attribute value keyPath '(null)'
    D restkit.object_mapping:RKMappingOperation.m:905 Mapping operation did not find any mappable values for the attribute and relationship mappings in the given object representation
    D restkit.object_mapping:RKMappingOperation.m:927 Failed mapping operation: No mappable values found for any of the attributes or relationship mappings
    W restkit.object_mapping:RKMappingOperation.m:553 WARNING: Failed mapping nested object: No mappable values found for any of the attributes or relationship mappings
  

Отладчик отображает эти сопоставления в -(BOOL)mapOneToManyRelationshipWithValue:отображение:

     (lldb) po relationshipMapping
<RKRelationshipMapping: 0x93d8330 amount => amount>

(lldb) po relationshipMapping.mapping
<RKEntityMapping:0x93b7d50 objectClass=TSNDecimal propertyMappings=
        (
        "<RKAttributeMapping: 0x93b8e60 (null) => number>"
        )>
  

отладка объекта-> сопоставление JSON

ОБНОВЛЕНИЕ 1

Я заметил, что когда TSNDecimal сериализуется (или непосредственно перед процессом сопоставления), он снова создается в RKMappingOperation.m, in -(id)destinationObjectForMappingRepresentation:parentRepresentation:withMapping:inRelationship: on line

 return [self.dataSource mappingOperation:self targetObjectForRepre ...
  

Воссоздан TSNDecimalNumber

с неправильным конструктором new (вот почему я вижу ошибку coredata: CoreData: ошибка: не удалось вызвать назначенный инициализатор в классе NSManagedObject) позже в журнале. Self.DataSource вызывает неправильную операцию сопоставления:targetObjectForRepresentation: поскольку это источник данных для неуправляемых объектов (RKObjectMappingOperationDataSource). Это должен быть RKManagedObjectMappingOperationDataSource .

Я предположил, что это может быть связано с проблемой RKObjectMapping v. RKEntityMapping. Но когда я использую RKEntityMapping для TSNTagAmountRequest:

 tagAmountRequestMapping = [RKEntityMapping mappingForEntityForName:[TSNTagAmountRequest class] inManagedObjectStore:managedObjectStore]; 
  

затем я получаю сообщение об ошибке: ** Завершение работы приложения из-за неперехваченного исключения ‘NSInvalidArgumentException’, причина: ‘ RKRequestDescriptor объекты должны быть инициализированы отображением, целевой класс которого NSMutableDictionary , получил ‘TSNTagAmountRequest’ (см. [RKObjectMapping requestMapping] )’**

ОБНОВЛЕНИЕ 2

Я изменил реализацию так, чтобы она использовала обратные сопоставления:

     RKObjectMapping* decimalMappingInverse        = [(RKEntityMapping*)decimalMapping inverseMapping];

    RKPropertyMapping* decimalPropertyMappingAmountInverse =
                  [RKRelationshipMapping relationshipMappingFromKeyPath:@"amount"
                                                              toKeyPath:@"amount"
                                                   withMapping:decimalMappingInverse];

    [tagAmountRequestMapping addPropertyMapping:decimalPropertyMappingAmountInverse];


requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:tagAmountRequestMapping
                                                          objectClass:[TSNTagAmountRequest class]
                                                          rootKeyPath:nil
                                                               method:RKRequestMethodPOST];
[_rkManager addRequestDescriptor:requestDescriptor];
  

С выводом с исключением в конце операции сопоставления недопустимого (нестрокового) ключа в словаре JSON:

  (
 {
     5 =         {
     };
 },
 {
     100 =         {
     };
 }
 )
  

И журнал:

 D restkit.object_mapping:RKMappingOperation.m:860 Starting mapping operation...
T restkit.object_mapping:RKMappingOperation.m:861 Performing mapping operation: <RKMappingOperation 0xf844d50> for '__NSDictionaryM' object. Mapping values from object <TSNTicketAmountApiRequest: 0xf80e9b0> (entity: TSNTicketAmountApiRequest; id: 0xf86bbe0 <x-coredata:///TSNTicketAmountApiRequest/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F66> ; data: {
                                                                                                                                                                                                                                                                   amount =     (
                                                                                                                                                                                                                                                                                 "0xf8c3b30 <x-coredata:///TSNDecimalApiTO/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F67>",
                                                                                                                                                                                                                                                                                 "0xf80c370 <x-coredata:///TSNDecimalApiTO/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F68>"
                                                                                                                                                                                                                                                                                 );

                                                                                                                                                                                                                                                                   }) ((null)) to object {
} with object mapping (null)
T restkit.object_mapping:RKMappingOperation.m:454 Mapping attribute value keyPath 'token' to 'token'
T restkit.object_mapping:RKMappingOperation.m:470 Mapped attribute value from keyPath 'token' to 'token'. Value: SXppH1ajA9OspKHkQ6Uwi7dD7AoYVP7J:api
T restkit.object_mapping:RKMappingOperation.m:454 Mapping attribute value keyPath 'bonification' to 'bonification'
T restkit.object_mapping:RKMappingOperation.m:470 Mapped attribute value from keyPath 'bonification' to 'bonification'. Value: 1
D restkit.network:RKObjectParameterization.m:129 Serialized NSDecimalNumber value at keyPath to __NSCFString (1)
T restkit.object_mapping:RKMappingOperation.m:454 Mapping attribute value keyPath 'isNetCurrency' to 'isNetCurrency'
T restkit.object_mapping:RKMappingOperation.m:470 Mapped attribute value from keyPath 'isNetCurrency' to 'isNetCurrency'. Value: 0
D restkit.network:RKObjectParameterization.m:129 Serialized __NSCFNumber value at keyPath to __NSCFBoolean (0)
D restkit.object_mapping:RKMappingOperation.m:641 Mapping one to many relationship value at keyPath 'amount' to 'amount'
T restkit.object_mapping:RKMappingOperation.m:542 Performing nested object mapping using mapping <RKRelationshipMapping: 0xf87bf40 amount => amount> for data: <TSNDecimalApiTO: 0xf8f0a60> (entity: TSNDecimalApiTO; id: 0xf8c3b30 <x-coredata:///TSNDecimalApiTO/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F67> ; data: {
                                                                                                                                                                                                                                                number = 5;
                                                                                                                                                                                                                                                })
D restkit.object_mapping:RKMappingOperation.m:860 Starting mapping operation...
T restkit.object_mapping:RKMappingOperation.m:861 Performing mapping operation: <RKMappingOperation 0x93779f0> for '__NSDictionaryM' object. Mapping values from object <TSNDecimalApiTO: 0xf8f0a60> (entity: TSNDecimalApiTO; id: 0xf8c3b30 <x-coredata:///TSNDecimalApiTO/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F67> ; data: {
                                                                                                                                                                                                                                                         number = 5;
                                                                                                                                                                                                                                                         }) ({
    mapping =     {
        collectionIndex = 3221208359;
    };
}) to object {
} with object mapping (null)
 T restkit.object_mapping:RKMappingOperation.m:439 Found transformable value at keyPath 'number'. Transforming from class 'NSDecimalNumber' to 'NSMutableDictionary'
 T restkit.object_mapping:RKMappingOperation.m:454 Mapping attribute value keyPath 'number' to '(null)'
 T restkit.object_mapping:RKMappingOperation.m:470 Mapped attribute value from keyPath 'number' to '(null)'. Value: {
    5 =     {
    };
}
 D restkit.object_mapping:RKMappingOperation.m:929 Finished mapping operation successfully...
 T restkit.object_mapping:RKMappingOperation.m:542 Performing nested object mapping using mapping <RKRelationshipMapping: 0xf87bf40 amount => amount> for data: <TSNDecimalApiTO: 0xf8f0ea0> (entity: TSNDecimalApiTO; id: 0xf80c370 <x-coredata:///TSNDecimalApiTO/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F68> ; data: {
                                                                                                                                                                                                                                                number = 100;
                                                                                                                                                                                                                                                })
 D restkit.object_mapping:RKMappingOperation.m:860 Starting mapping operation...
 T restkit.object_mapping:RKMappingOperation.m:861 Performing mapping operation: <RKMappingOperation 0x9311160> for '__NSDictionaryM' object. Mapping values from object <TSNDecimalApiTO: 0xf8f0ea0> (entity: TSNDecimalApiTO; id: 0xf80c370 <x-coredata:///TSNDecimalApiTO/t5BFB6379-F8EB-4367-A53B-E1B3C531A34F68> ; data: {
                                                                                                                                                                                                                                                         number = 100;
                                                                                                                                                                                                                                                         }) ({
    mapping =     {
        collectionIndex = 3221208359;
    };
}) to object {
} with object mapping (null)
 T restkit.object_mapping:RKMappingOperation.m:439 Found transformable value at keyPath 'number'. Transforming from class 'NSDecimalNumber' to 'NSMutableDictionary'
 T restkit.object_mapping:RKMappingOperation.m:454 Mapping attribute value keyPath 'number' to '(null)'
 T restkit.object_mapping:RKMappingOperation.m:470 Mapped attribute value from keyPath 'number' to '(null)'. Value: {

    };
}
 D restkit.object_mapping:RKMappingOperation.m:929 Finished mapping operation successfully...
 T restkit.object_mapping:RKMappingOperation.m:682 Mapped relationship object from keyPath 'amount' to 'amount'. Value: (
                                                                                                                                                                           {
                                                                                                                                                                               5 =         {
                                                                                                                                                                               };
                                                                                                                                                                           },
                                                                                                                                                                           {
                                                                                                                                                                               100 =         {
                                                                                                                                                                               };
                                                                                                                                                                           }
                                                                                                                                                                           )
  

В дополнение к вышеупомянутому исключению результат не соответствует требуемому телу {«сумма»: [5,100]}:

введите описание изображения здесь

  D restkit.object_mapping:RKMappingOperation.m:929 Finished mapping operation successfully...
 exception Invalid (non-string) key in JSON dictionary
  

ОБНОВЛЕНИЕ 3

Эта проблема сводится к тому, как массив десятичных чисел из JSON анализируется в NSSet NSManagedObjects (работает с самого первого момента) и наоборот, из NSSet в массив JSON (довольно сложно заставить его работать):

 {"amount":[10,20,50]}  
  

это

 TSNTagAmountRequest:{amount:[TSNDecimal, TSNDecimal, TSNDecimal, ...]}
  

Где

 @interface TSNTagAmountRequest : NSManagedObject
@property(nonatomic) NSSet *amount;
@end
  

и

 @interface TSNDecimal : NSManagedObject
@property(nonatomic) NSDecimalNumber* number; // is a helper property
@end

RKEntityMapping* tagAmountRequestMapping = [RKObjectMapping requestMapping];

[tagAmountRequestMapping  
      addPropertyMapping:decimalPropertyMappingAmountInverse];
  

где

     RKPropertyMapping* decimalPropertyMappingAmountInverse =
                              [RKRelationshipMapping 
                                relationshipMappingFromKeyPath:@"amount"
                                                     toKeyPath:@"amount"
                                                   withMapping:decimalMappingInverse];
  

где

  RKObjectMapping* decimalMappingInverse = [(RKEntityMapping*)decimalMapping inverseMapping];
  

где

  RKObjectMapping* decimalMapping = [RKEntityMapping mappingForEntityForName:@"TSNDecimal" inManagedObjectStore:managedObjectStore];
 [mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil          
                                                                   toKeyPath:@"number"]];


    requestDescriptor = [RKRequestDescriptor 
                      requestDescriptorWithMapping:tagAmountRequestMapping
                                       objectClass:[TSNTagAmountRequest class]
                                    rootKeyPath:TSNServiceClientRemoteRootKeyPathNotNested
                                         method:RKRequestMethodPOST];
[_rkManager addRequestDescriptor:requestDescriptor];
  

Метод (NSData *)dataFromObject:ошибка: из RKNSJSONSerialization не может использовать результирующий объект из сопоставления, содержащего NSDictionary :

  (
 {
     5 =         {
     };
 },
 {
     100 =         {
     };
 }
 )
  

где я ожидаю чего-то вроде [5, 100] .

Приложение завершает работу с недопустимым (нестроковым) ключом в словаре JSON.

Я не понимаю, почему [NSJSONSerialization dataWithJSONObject:параметры объекта:0 ошибка:ошибка]; подается этот странный массив словарей вместо массива NSDecimalNumbers .

ОБНОВЛЕНИЕ 4

Я изменил определение сопоставления отношений между TSNTagAmountRequest и TSNDecimal, чтобы значение в TSNDecimal (элемент из amount NSSet) распространялось с его атрибутом «number».

 RKPropertyMapping* decimalPropertyMappingAmountInverse =
                              [RKRelationshipMapping relationshipMappingFromKeyPath:@"amount.number"
                                                                          toKeyPath:@"amount"
                                                                        withMapping:decimalApiMappingInverse];
  

Движок Restkit, точно применяющий метод relationshipmappings из RKMappingOperation.m может извлекать значения сумм 5, 100, но конечный JSON в любом случае собран неправильно:

 "amount":[{},{}]
  

с отсутствующими значениями. Я почти на месте.

ОБНОВЛЕНИЕ 5

После настройки обратного сопоставления для всех задействованных объектов, подачи дескриптора запроса, все равно вывод (управляемый номером сопоставления-> nil) неправильно собран со словарем словарей, а не с массивом JSON. Единственный способ — подкласс RKNSJSONSerialization и исправить структуру, то есть перевести словарь словарей в простой nsarray, который совместим с сериализатором ns json.

Комментарии:

1. Why it is not possible to create the json body with RKRelationshipMapping — потому что создание JSON — это операция с дескриптором запроса, и вы используете только дескрипторы ответов. Кроме этого, на самом деле нет никаких ограничений. Я предлагаю вам показать реальный код и реальный JSON, который поступает или который вы хотите удалить, поскольку ваш текущий вопрос не имеет особого смысла.

2. Еще раз спасибо. Я обновил вопрос. Я пытался решить эту проблему, но не смог найти способ самостоятельно.

3. По-прежнему безуспешно. Должно быть что-то очевидное при сериализации NSSet NSManagedObjects в массив JSON.

4. Похоже, вы добиваетесь прогресса, но у вас путаница между назначением дескриптора запроса и ответа и сопоставлениями с нулевым ключом. Как правило, создайте отображение ответа и дескриптор, затем возьмите обратное отображение и создайте дескриптор запроса. В сопоставлении ответов должен использоваться путь с нулевым ключом, а обратный, как правило, не должен быть изменен каким-либо образом.

5. Я думаю, что инструкции со страницы Restkit ( github.com/RestKit/RestKit/wiki /… ) не работают для обратного отображения. Я не видел кода, который работает. Но мне нужно это подтвердить. Я предполагаю, что обратное сопоставление для [RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@»number»]] является [RKAttributeMapping attributeMappingFromKeyPath:@»number» toKeyPath:nil]] .