Having problems with Restkit nested mappings

2019-09-05 16:54发布

问题:

I've been trying to create a request to a login service, but i'm having problems when receiving the response, and i don't know what i'm doing wrong.

Here's my code:

AppDelagate

/* LOGIN RESPONSE */
    RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[UserMapping class]];
    [userMapping addAttributeMappingsFromDictionary:@{ @"_id":@"_id", @"name": @"name", @"lastname": @"lastname", @"username": @"username", @"password": @"password", @"repeatPassword": @"repeatPassword", @"age": @"age", @"gender": @"gender", @"photo": @"photo"}];

    RKObjectMapping *loginResponseMapping = [RKObjectMapping mappingForClass:[LoginResponse class]];
    [loginResponseMapping addAttributeMappingsFromArray:@[ @"code", @"message" ]];
    [loginResponseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"data" toKeyPath:@"data" withMapping:userMapping]];

    RKResponseDescriptor *loginResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:loginResponseMapping method:RKRequestMethodPOST pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];

    [manager addResponseDescriptor:baseResponseDescriptor];
    [manager addRequestDescriptor:loginRequestDescriptor];
    [manager addResponseDescriptor:loginResponseDescriptor];
    [manager addRequestDescriptor:signupRequestDescriptor];

    /* SERIALIZATION TYPE */
    [manager setRequestSerializationMIMEType:RKMIMETypeJSON];

LoginResponse class

@interface LoginResponse : NSObject

@property (nonatomic, strong) NSNumber *code;
@property (nonatomic, strong) NSString *message;
@property (nonatomic, strong) UserMapping *data;

@end

UserMapping class

@interface UserMapping : NSObject

@property (nonatomic, strong) NSString *_id;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *lastname;
@property (nonatomic, strong) NSString *age;
@property (nonatomic, strong) NSString *gender;
@property (nonatomic, strong) NSString *username;
@property (nonatomic, strong) NSString *password;
@property (nonatomic, strong) NSString *repeatPassword;
@property (nonatomic, strong) NSString *photo;

@end

LOG:

2014-03-07 14:56:19.439 Shoopi[8149:a0b] D restkit.object_mapping:RKMappingOperation.m:952 Starting mapping operation...
2014-03-07 14:56:19.440 Shoopi[8149:a0b] T restkit.object_mapping:RKMappingOperation.m:953 Performing mapping operation: <RKMappingOperation 0xbb711f0> for '__NSDictionaryM' object. Mapping values from object <LoginRequest: 0xbbcfb20> ((null)) to object {
} with object mapping (null)
2014-03-07 14:56:19.441 Shoopi[8149:a0b] T restkit.object_mapping:RKMappingOperation.m:550 Mapping attribute value keyPath 'username' to 'username'
2014-03-07 14:56:19.442 Shoopi[8149:410b] D restkit.object_mapping:RKPropertyInspector.m:130 Cached property inspection for Class 'NSMutableDictionary': {
    fileHFSFlags =     {
        isPrimitive = 1;
        keyValueCodingClass = NSNumber;
        name = fileHFSFlags;
    };
    fileHFSResourceForkSize =     {
        isPrimitive = 1;
        keyValueCodingClass = NSNumber;
        name = fileHFSResourceForkSize;
    };
}
2014-03-07 14:56:19.442 Shoopi[8149:a0b] T restkit.object_mapping:RKMappingOperation.m:572 Mapped attribute value from keyPath 'username' to 'username'. Value: test@test.cl
2014-03-07 14:56:19.443 Shoopi[8149:a0b] T restkit.object_mapping:RKMappingOperation.m:550 Mapping attribute value keyPath 'password' to 'password'
2014-03-07 14:56:19.443 Shoopi[8149:410b] D restkit.object_mapping:RKPropertyInspector.m:130 Cached property inspection for Class 'LoginRequest': {
    password =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = password;
    };
    username =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = username;
    };
}
2014-03-07 14:56:19.445 Shoopi[8149:a0b] T restkit.object_mapping:RKMappingOperation.m:572 Mapped attribute value from keyPath 'password' to 'password'. Value: 1234
2014-03-07 14:56:19.446 Shoopi[8149:a0b] D restkit.object_mapping:RKMappingOperation.m:1021 Finished mapping operation successfully...
2014-03-07 14:56:19.450 Shoopi[8149:a0b] AJUA !
2014-03-07 14:56:19.456 Shoopi[8149:a0b] T restkit.network:RKObjectRequestOperation.m:178 POST 'http://shoppi-services.herokuapp.com/public/login':
request.headers={
    Accept = "application/json";
    "Accept-Language" = "en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5";
    "Content-Type" = "application/json; charset=utf-8";
    "User-Agent" = "Shoopi/1.0 (iPhone Simulator; iOS 7.0; Scale/2.00)";
}
request.body={"username":"test@test.cl","password":"1234"}
2014-03-07 14:56:19.981 Shoopi[8149:410b] D restkit.object_mapping:RKMapperOperation.m:377 Executing mapping operation for representation: {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
}
 and targetObject: (null)
2014-03-07 14:56:19.982 Shoopi[8149:410b] T restkit.object_mapping:RKMapperOperation.m:320 Examining keyPath '' for mappable content...
2014-03-07 14:56:19.982 Shoopi[8149:410b] D restkit.object_mapping:RKMapperOperation.m:300 Found mappable data at keyPath '': {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
}
2014-03-07 14:56:19.983 Shoopi[8149:410b] D restkit.object_mapping:RKMapperOperation.m:231 Asked to map source object {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
} with mapping <RKObjectMapping:0x8c9e1e0 objectClass=BaseModel propertyMappings=(
    "<RKAttributeMapping: 0x8cadf20 code => code>",
    "<RKAttributeMapping: 0x8ca6920 message => message>"
)>
2014-03-07 14:56:19.984 Shoopi[8149:410b] D restkit.object_mapping:RKMappingOperation.m:952 Starting mapping operation...
2014-03-07 14:56:19.984 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:953 Performing mapping operation: <RKMappingOperation 0xbb6db80> for 'BaseModel' object. Mapping values from object {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
} to object <BaseModel: 0xbbf2b80> with object mapping (null)
2014-03-07 14:56:19.985 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:550 Mapping attribute value keyPath 'code' to 'code'
2014-03-07 14:56:19.986 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:572 Mapped attribute value from keyPath 'code' to 'code'. Value: 0
2014-03-07 14:56:19.986 Shoopi[8149:1007] D restkit.object_mapping:RKPropertyInspector.m:130 Cached property inspection for Class 'BaseModel': {
    code =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = code;
    };
    message =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = message;
    };
}
2014-03-07 14:56:19.986 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:550 Mapping attribute value keyPath 'message' to 'message'
2014-03-07 14:56:19.986 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:572 Mapped attribute value from keyPath 'message' to 'message'. Value: Success
2014-03-07 14:56:19.987 Shoopi[8149:410b] D restkit.object_mapping:RKMappingOperation.m:1021 Finished mapping operation successfully...
2014-03-07 14:56:19.987 Shoopi[8149:410b] T restkit.object_mapping:RKMapperOperation.m:320 Examining keyPath '<null>' for mappable content...
2014-03-07 14:56:19.988 Shoopi[8149:410b] D restkit.object_mapping:RKMapperOperation.m:300 Found mappable data at keyPath '<null>': {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
}
2014-03-07 14:56:19.989 Shoopi[8149:410b] D restkit.object_mapping:RKMapperOperation.m:231 Asked to map source object {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
} with mapping <RKObjectMapping:0x8caafa0 objectClass=LoginResponse propertyMappings=(
    "<RKAttributeMapping: 0x8cab020 code => code>",
    "<RKAttributeMapping: 0x8cab030 message => message>",
    "<RKRelationshipMapping: 0x8cab240 data => data>"
)>
2014-03-07 14:56:19.989 Shoopi[8149:410b] D restkit.object_mapping:RKMappingOperation.m:952 Starting mapping operation...
2014-03-07 14:56:19.990 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:953 Performing mapping operation: <RKMappingOperation 0x8ccb570> for 'LoginResponse' object. Mapping values from object {
    code = 0;
    data = "{\"_id\":\"5319459fe6b6558526000001\",\"age\":\"19\",\"gender\":\"female\",\"lastname\":\"test\",\"name\":\"test\",\"password\":\"1234\",\"photo\":null,\"repeatPassword\":\"1234\",\"username\":\"test@test.cl\"}";
    message = Success;
} to object <LoginResponse: 0xb9adc50> with object mapping (null)
2014-03-07 14:56:19.990 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:550 Mapping attribute value keyPath 'code' to 'code'
2014-03-07 14:56:19.990 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:572 Mapped attribute value from keyPath 'code' to 'code'. Value: 0
2014-03-07 14:56:19.990 Shoopi[8149:1007] D restkit.object_mapping:RKPropertyInspector.m:130 Cached property inspection for Class 'LoginResponse': {
    code =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = code;
    };
    data =     {
        isPrimitive = 0;
        keyValueCodingClass = UserMapping;
        name = data;
    };
    message =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = message;
    };
}
2014-03-07 14:56:19.991 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:550 Mapping attribute value keyPath 'message' to 'message'
2014-03-07 14:56:19.991 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:572 Mapped attribute value from keyPath 'message' to 'message'. Value: Success
2014-03-07 14:56:19.992 Shoopi[8149:410b] D restkit.object_mapping:RKMappingOperation.m:683 Mapping one to one relationship value at keyPath 'data' to 'data'
2014-03-07 14:56:19.992 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:641 Performing nested object mapping using mapping <RKRelationshipMapping: 0x8cab240 data => data> for data: {"_id":"5319459fe6b6558526000001","age":"19","gender":"female","lastname":"test","name":"test","password":"1234","photo":null,"repeatPassword":"1234","username":"test@test.cl"}
2014-03-07 14:56:19.993 Shoopi[8149:410b] D restkit.object_mapping:RKMappingOperation.m:952 Starting mapping operation...
2014-03-07 14:56:19.993 Shoopi[8149:410b] T restkit.object_mapping:RKMappingOperation.m:953 Performing mapping operation: <RKMappingOperation 0x8ccc990> for 'UserMapping' object. Mapping values from object {"_id":"5319459fe6b6558526000001","age":"19","gender":"female","lastname":"test","name":"test","password":"1234","photo":null,"repeatPassword":"1234","username":"test@test.cl"} ({
    HTTP =     {
        request =         {
            URL = "http://shoppi-services.herokuapp.com/public/login";
            headers =             {
                Accept = "application/json";
                "Accept-Language" = "en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5";
                "Content-Type" = "application/json; charset=utf-8";
                "User-Agent" = "Shoopi/1.0 (iPhone Simulator; iOS 7.0; Scale/2.00)";
            };
            method = POST;
        };
        response =         {
            URL = "http://shoppi-services.herokuapp.com/public/login";
            headers =             {
                Connection = "keep-alive";
                "Content-Length" = 250;
                "Content-Type" = "application/json;charset=utf-8";
                Date = "Fri, 07 Mar 2014 17:56:19 GMT";
                Server = "WEBrick/1.3.1 (Ruby/2.1.0/2013-12-25)";
                "Set-Cookie" = "SHOPPI-TOKEN=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTZkY2FmNDQ2NGU1MDNhYjJmYzdl%0AMDA3ZDczOGQ0OTQ0NTI4YmFmMTE0NTY5OTU0YmFjMTE3NWNhYjE4OTFkMjAG%0AOwBGSSIUc2luYXRyYS5zZXNzaW9uBjsAVFQ%3D%0A--dafa7a330f49e7c69ca1fd8d0768a889dd2345d2; path=/; expires=Fri, 07 Mar 2014 18:56:19 -0000; HttpOnly";
                Via = "1.0 proxy2.taisagroup.com (squid)";
                "X-Cache" = "MISS from proxy2.taisagroup.com";
                "X-Cache-Lookup" = "MISS from proxy2.taisagroup.com:3128";
                "X-Content-Type-Options" = nosniff;
            };
        };
    };
    mapping =     {
        collectionIndex = "<null>";
        rootKeyPath = "<null>";
    };
}) to object <UserMapping: 0x8ccb720> with object mapping (null)
2014-03-07 14:56:20.045 Shoopi[8149:410b] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0xbb6e580> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.'
*** First throw call stack:
(
    0   CoreFoundation                      0x024e95e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x0226c8b6 objc_exception_throw + 44
    2   CoreFoundation                      0x025796a1 -[NSException raise] + 17
    3   Foundation                          0x01f2db0a -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 282
    4   Foundation                          0x01e9ab61 _NSGetUsingKeyValueGetter + 81
    5   Foundation                          0x01e9a19b -[NSObject(NSKeyValueCoding) valueForKey:] + 260
    6   Foundation                          0x01eb9c9a -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 409
    7   Shoopi                              0x000d2571 -[RKMappingSourceObject valueForKeyPath:] + 833
    8   Shoopi                              0x000d7b87 -[RKMappingOperation applyAttributeMappings:] + 1671
    9   Shoopi                              0x000df57e -[RKMappingOperation main] + 4062
    10  Foundation                          0x01f41a69 -[__NSOperationInternal _start:] + 671
    11  Foundation                          0x01ebe798 -[NSOperation start] + 83
    12  Shoopi                              0x000d8a0a -[RKMappingOperation mapNestedObject:toObject:withRelationshipMapping:metadata:] + 1706
    13  Shoopi                              0x000d9655 -[RKMappingOperation mapOneToOneRelationshipWithValue:mapping:] + 1477
    14  Shoopi                              0x000dd2e6 -[RKMappingOperation applyRelationshipMappings] + 6870
    15  Shoopi                              0x000df620 -[RKMappingOperation main] + 4224
    16  Foundation                          0x01f41a69 -[__NSOperationInternal _start:] + 671
    17  Foundation                          0x01ebe798 -[NSOperation start] + 83
    18  Shoopi                              0x000ccbe5 -[RKMapperOperation mapRepresentation:toObject:atKeyPath:usingMapping:metadata:] + 1957
    19  Shoopi                              0x000cb460 -[RKMapperOperation mapRepresentation:atKeyPath:usingMapping:] + 1904
    20  Shoopi                              0x000cdddd -[RKMapperOperation mapRepresentationOrRepresentations:atKeyPath:usingMapping:] + 829
    21  Shoopi                              0x000ce732 -[RKMapperOperation mapSourceRepresentationWithMappingsDictionary:] + 2210
    22  Shoopi                              0x000cf0fb -[RKMapperOperation main] + 1403
    23  Foundation                          0x01f41a69 -[__NSOperationInternal _start:] + 671
    24  Foundation                          0x01ebe798 -[NSOperation start] + 83
    25  Shoopi                              0x0011f7ba -[RKObjectResponseMapperOperation performMappingWithObject:error:] + 1354
    26  Shoopi                              0x0011d7b3 -[RKResponseMapperOperation main] + 2371
    27  Foundation                          0x01f41a69 -[__NSOperationInternal _start:] + 671
    28  Foundation                          0x01ebe798 -[NSOperation start] + 83
    29  Foundation                          0x01f43d34 __NSOQSchedule_f + 62
    30  libdispatch.dylib                   0x0310f4b0 _dispatch_client_callout + 14
    31  libdispatch.dylib                   0x030fd088 _dispatch_queue_drain + 450
    32  libdispatch.dylib                   0x030fce85 _dispatch_queue_invoke + 126
    33  libdispatch.dylib                   0x030fde25 _dispatch_root_queue_drain + 83
    34  libdispatch.dylib                   0x030fe13d _dispatch_worker_thread2 + 39
    35  libsystem_c.dylib                   0x03427e72 _pthread_wqthread + 441
    36  libsystem_c.dylib                   0x0340fd2a start_wqthread + 30
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

Regards.

回答1:

You have a JSON string inside your JSON payload. RestKit isn't going to play nice with that. Ideally you should change the server. Otherwise you would need to read data in as the string it is and post-process it...

That post processing could use RestKit by creating and executing a RKMappingOperation, but changing the source JSON is far preferable if possible as using RKMappingOperation will make relationships harder to manage.


Initial wrong answer, but important anyway:

You need to correct all of your path patterns and keypaths (mostly path patterns). At the moment you're asking RestKit to apply all mappings to responses and eventually (as you see) something will go wrong. You use path patterns to limit which response descriptors are applied to which responses.