RestKit complex and simple JSON RKObjectMapping almost working, but

若如初见. 提交于 2020-01-16 00:02:17

问题


I have been researching this issue.  Here are a few discoveries that will help map this JSON response in RestKit

The JSON response object contains three top level objects : locations is an array cityKey object
stateKey object

Since Restkit is written in Objective-C, I looked at it as if I were going to directing map these objects and parse-out data

I wrote the following code to map the NSDictionary portion of the Location Class\Object:

    RKObjectMapping* locationMapping = [RKObjectMapping       mappingForClass:[Location class]];
     [locationMapping addAttributeMappingsFromDictionary:@{
                             @"distance": @"distance",
                             @"major": @"major",
                             @"minor": @"minor" }];

I wrote the following code for the overall class\object , Locations: //This should also be an NSDictionary

    RKObjectMapping *locationsMapping = RKObjectMapping       mappingforclass: [Locations class]];

This appears to be a mapping array should be a dictionary.

    [locationsMapping addAttributeMappingsFromArray:@[@"locations", @"cityKey",@"stateKey"]];

I see the error in the use of Array for the overall object Locations. This runs and returns a partially successful result from the following:

RKResponseDescriptor *locationDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationMapping method:RKRequestMethodAny pathPattern:@"/location" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [[RKObjectManager sharedManager]    addResponseDescriptor:responseDescriptor];
    [[RKObjectManager sharedManager]    addResponseDescriptor:locationDescriptor];



[[RKObjectManager sharedManager] postObject:nil path:(_enterLocation) parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
     NSLog(@"It Worked: %@", [mappingResult array]);


} failure:^(RKObjectRequestOperation *operation, NSError *error) {
     NSLog(@"It Failed: %@", error);

  }];

This returns the successful responseJSONdata, but mappingResult dictionary returns:

2014-05-07 17:28:39.770 MyApp[163:60b] It Worked: {
    locations =  (
    );

}

The actual JSONOBJECTWITHDATA returned as response.body is:

response.body={
  "locations": [
    {
      "distance": 0.5, 
      "major": 2, 
      "minor": 4, 
      
    }, 
    {
      "distance": 1.0, 
      "major": 2, 
      "minor": 11, 
      
    }
  ], 
  "cityKey": "12", 
  "stateKey": "41"
}

From reading,

Will RestKit's dynamic mapping solve this complex JSON mapping?

and

Feeding parsed data to RKMapperOperation throws NSUnknownKeyException

I know that I need to change my approach to RestKit mapping. For the complex case

Even in the simple case, I am only returning a pointer to the object.

 RKObjectMapping *statusResponseMapping = [RKObjectMapping mappingForClass:[StatusResponse class]];

    [statusResponseMapping addAttributeMappingsFromDictionary:@{@"status":@"status"}]; 

RKResponseDescriptor *statusResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:statusResponseMapping method:RKRequestMethodPOST pathPattern:nil keyPath:@"response" statusCodes:statusCodes];

I receive the correct response from the server...but only get a pointer

 response.body={
      "response": {
        "status": "ok"
      }
    }
    2014-05-07 17:11:47.362 MyApp[145:60b] It Worked: {
        response = "<StatusResponse: 0x16ee2e10>";

    }

I am missing key step or mapping definition. I am almost to the desired result.

What do I need to do to get the desired results in both the simple and complex cases?

UPDATED MY QUESTION WITH THE NEW INFORMATION BELOW

Updated today may 8 2014, I changed locationsMapping code to...

RKObjectMapping *locationsMapping = [RKObjectMapping mappingForClass:[Locations class]];
    [locationsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations" toKeyPath:@"locations" withMapping:locationMapping]];

Which moved it a step closer to correctly mapping. The result was...

2014-05-08 13:28:17.795 MyApp[165:60b] It Worked: {
    locations =     (
        "<Locations: 0x14e69a10>",
        "<Locations: 0x14e695c0>"
    );

Which is almost correct. I appear to be missing mapping for the cityKey and StateKey. I have not defined the Description method. Here is the raw JSON data returned...

response.body={
  "locations": [
    {
      "distance": 0.6, 
      "major": 2, 
      "minor": 4 

    }, 
    {
      "distance": 1.0, 
      "major": 2, 
      "minor": 11 

    }
  ], 
  "cityKey": "11", 
  "stateKey": "21"
}

Mapping does not return the cityKey and stateKey...something is still missing in the mapping definition?

UPDATED MAY 9 2014 AFTER CHANGING locationMapping as suggested

RKObjectMapping *locationsMapping = [RKObjectMapping mappingForClass:[Locations class]];

    [locationsMapping addAttributeMappingsFromArray:@[
 @"cityKey",@"stateKey"]];

    [locationsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations" toKeyPath:@"locations" withMapping:locationMapping]];


response.body={
  "locations": [
    {
      "distance": 0.6, 
      "major": 2, 
      "minor": 4
    }, 
    {
      "distance": 1.0, 
      "major": 2, 
      "minor": 11
    }
  ], 
  "cityKey": "10", 
  "stateKey": "15"
}
2014-05-09 13:18:17.669 MyApp[211:60b] It Worked: {
    locations  =     (
        "<Locations: 0x1780341e0>",
        "<Locations: 0x170027c40>"
    );
}

Still missing cityKey and stateKey...not sure what to try next?

UPDATED 05/12/2014 TO ADDRESS RESPONSE DESCRIPTORS...

I had two response descriptors. I missed posting one in the original description.

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationMapping method:RKRequestMethodAny pathPattern:nil keyPath:@"locations"  statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
    RKResponseDescriptor *locationDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationsMapping method:RKRequestMethodAny pathPattern:@"/location" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
    
    [[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
    [[RKObjectManager sharedManager] addResponseDescriptor:locationDescriptor];

I changed the structure as recommended but I am still missing the cityKey and stateKey...should I use only one Response Descriptor?

I found an answer to the "use more than one ResponseDescriptor?" here in this article http://www.softwarepassion.com/parsing-complex-json-with-objective-c-and-restkit/

When I reviewed how the author defined his POJOs, I discovered that only one ResponseDescriptor was required. The rest is handled in the mapping and defining of relationships...

UPDATED MAY 15 2014

I tested the response descriptors. Only one was actually working to generate the "It worked" result. The locationDescriptor was not working at all. It generated an error when used alone. It was ignored when used as a part of the pair. The descriptor that consistently delivered the "It works" with locations data was responseDescriptor with the keyPath as @"locations".

I read http://blog.mobilejazz.cat/ios-using-kvc-to-parse-json/

I have a question about "...is the expected log output from a custom class that you create but which doesn't have a description method implementation." What is meant by description method implementation? What do I need to add to change the following into an NSArray or NSDictionary of mapping results? Could you share an example based on locations below?

2014-05-09 13:18:17.669 MyApp[211:60b] It Worked: {
        locations  =     (
            "<Locations: 0x1780341e0>",
            "<Locations: 0x170027c40>"
        );

UPDATED MAY 16 2014 ADDED SUGGESTED NSSTRING DESCRIPTION TO CLASS IMPLEMENTATION FILES

response.body={
  "response": {
    "status": "ok"
  }
}
2014-05-16 10:32:26.260 MyApp[202:60b] It Worked: {
    response = "status: ok";
}

Therefore, I count the NSString description method implementation answered and working correctly.

SECOND PART OF THE UPDATE IS WITH METHOD DESCRIPTORS IN PLACE I CAN SEE THAT I AM NOT GETTING THE MAPPED JSON TO DATA STRUCTURE VALUES INSTEAD I AM GETTING NULL

response.body={
  "locations": [
    {
      "distance": 0.0, 
      "major": 2, 
      "minor": 8, 

    }, 
    {
      "distance": 3.5, 
      "major": 2, 
      "minor": 9, 

    }, 
    {
      "distance": 2.6575364531836625, 
      "major": 2, 
      "minor": 10, 

    }
  ], 
  "cityKey": "10", 

  "stateKey": "21"
}
2014-05-16 09:48:08.328 MyApp[189:60b] It Worked: {
    locations =     (
        "location: (null) city: (null) state: (null)",
        "location: (null) city: (null) state: (null)",
        "location: (null) city: (null) state: (null)"
    );
}

First issue is that I should get a location { distance, major, minor} for the three locations in the array of dictionaries and one cityKey and one stateKey. Here is the mapping code

RKObjectMapping* locationMapping = [RKObjectMapping mappingForClass:[Location class]];
    [locationMapping addAttributeMappingsFromDictionary:@{
                                                        @"distance": @"distance",
                                                        @"major": @"major",
                                                        @"minor": @"minor"
                                                        }];


    RKObjectMapping *locationsMapping = [RKObjectMapping mappingForClass:[Locations class]];

    [locationsMapping addAttributeMappingsFromArray:@[ @"cityKey",@"stateKey"]];



    [locationsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations" toKeyPath:@"locations" withMapping:locationMapping]];

 //05/16/2014 this ResponseDesciptor is working
   RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationsMapping method:RKRequestMethodAny pathPattern:nil keyPath:@"locations" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];

Why is it returning multiple cityKey and stateKey for each location? Why are the results showing null when JSON data returns?

UPDATED MAY 19 2014 EXCEPTION THROWN when keyPath is set to nil and not @"locations"

2014-05-19 10:53:53.902 MyApp[245:4907] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Locations 0x17022d960> valueForUndefinedKey:]: this class is not key value coding-compliant for the key locations.'
*** First throw call stack:

CODE RAISING IT RestKit code line 364

if ([self.dataSource respondsToSelector:@selector(mappingOperationShouldSetUnchangedValues:)] && [self.dataSource mappingOperationShouldSetUnchangedValues:self]) return YES;

    id currentValue = [self.destinationObject valueForKeyPath:keyPath];
    if (currentValue == [NSNull null]) {
        currentValue = nil;
    }

Locations Class header file

#import <Foundation/Foundation.h>

@class Location;


@interface Locations : NSObject




@property (nonatomic, copy) Location *location;
@property (nonatomic, copy) NSString *cityKey;
@property (nonatomic, copy) NSString *stateKey;


@end

Locations Class Implementation file

#import "Locations.h"

@implementation Locations

-(NSString *)description {

    return [NSString stringWithFormat:@"location: %@ cityKey: %@ stateKey: %@", self.location, self.cityKey, self.stateKey];
}

@end   

CHANGED LOCATIONS CLASS PROPERTY FROM

@property (nonatomic, copy) Location *location; 

TO

@property (nonatomic, copy) NSMutableArray *locations;

RECEIVED THE DESIRED RESULT when keyPath is nil

response.body={
  "locations": [
    {
      "distance": 0.0, 
      "major": 2, 
      "minor": 8 

    }, 
    {
      "distance": 3.5, 
      "major": 2, 
      "minor": 9 

    }, 
    {
      "distance": 2.6575364531836625, 
      "major": 2, 
      "minor": 10 

    }
  ], 
  "cityKey": "1", 

  "stateKey": "1"
}
2014-05-19 14:19:45.040 MyApp[290:60b] It Worked: {
    "<null>" = "location: (\n    \"distance: 0 major: 2  minor: 8 \",\n    \"distance: 3.5 major: 2  minor: 9 \",\n    \"distance: 2.657536453183662 major: 2  minor: 10 \"\n) cityKey: 1 stateKey: 1";
}

Which fixed this problem.


回答1:


When you create locationsMapping, @"locations" should be added as a relationship using locationMapping (not directly as an attribute).

You should have a response descriptor that uses locationsMapping.

This:

2014-05-07 17:11:47.362 MyApp[145:60b] It Worked: {
    response = "<StatusResponse: 0x16ee2e10>";
}

is the expected log output from a custom class that you create but which doesn't have a description method implementation.


RKObjectMapping *locationsMapping = [RKObjectMapping mappingForClass:[Locations class]];
[locationsMapping addAttributeMappingsFromArray:@[@"cityKey", @"stateKey"]];
[locationsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations" toKeyPath:@"locations" withMapping:locationMapping]];

On your Locations class add:

- (NSString *)description
{
    return [NSString stringWithFormat:@"city:%@ state:%@ locations:%@", self.cityKey, self.stateKey, self.locations];
}

and a similar method to Location class.



来源:https://stackoverflow.com/questions/23529494/restkit-complex-and-simple-json-rkobjectmapping-almost-working-but

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!