I'm having a problem mapping a JSON array of strings with no key paths using RestKit 0.20.3. My classes are based on the the example in the RestKit wiki.
JSON:
"feature_list":{
"property":["test","test ","test","test"],
"street":["test1","foo","bar"],
"garden":["foo","bar"],
"other":["foo","bar", "test2"]
}
Classes:
#interface FeatureList : NSManagedObject
#property(nonatomic, strong) NSSet *property;
#property(nonatomic, strong) NSSet *street;
#property(nonatomic, strong) NSSet *garden;
#property(nonatomic, strong) NSSet *other;
#end
#interface Feature : NSManagedObject
#property(nonatomic, strong) NSString *featureType;
#end
Mapping Setup:
+ (RKMapping *)featureListMapping {
RKObjectMapping *mapping = [RKEntityMapping mappingForEntityForName:#"FeatureList" inManagedObjectStore:objectStore];
[mapping addRelationshipMappingWithSourceKeyPath:#"property" mapping:[self featureMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"street" mapping:[self featureMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"garden" mapping:[self featureMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"other" mapping:[self featureMapping]];
}
+ (RKMapping *)featureMapping {
RKObjectMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Feature" inManagedObjectStore:objectStore];
[mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"featureType"]];
return mapping;
}
When debugging the feature.featureType object in Xcode, a RKMappingSourceObject object is returned with the value stored in an object property, which I can't access.
When printing feature.featureType.class, NSCFString is printed.
for (Feature * feature in featureList.property)
{
//feature.featureType is a RKMappingSourceObject in the debugger
NSString *featureStr = feature.featureType.class; //prints NSCFString
}
Log output:
Mapped attribute value from keyPath '(null)' to 'featureType'. Value: test ({
HTTP = {
request = {
URL = "http://localhost:3000/api/v1/test";
headers = {
};
method = GET;
};
response = {
URL = "http://localhost:3000/api/v1/test";
headers = {
"Cache-Control" = "max-age=0, private, must-revalidate";
"Content-Type" = "application/json; charset=utf-8";
Etag = "\"193d0f470155872e5b36e9f7586c0c8f\"";
"Proxy-Connection" = Close;
Server = "thin 1.5.0 codename Knife";
"X-Request-Id" = 0e4de78e656ef2165699385695cdfe75;
"X-Runtime" = "0.092788";
"X-UA-Compatible" = "IE=Edge";
};
};
};
mapping = {
collectionIndex = 1;
rootKeyPath = response;
};
})
Any suggestions would be appreciated, Thanks
Basically, you can use any method on the returned proxy object other than description. That means not supplying it as a variable to NSLog.
The true objects will be stored into the data store. Depending on what you need to do you may want to go and retrieve them directly (by fetching or using the managed object id).
I don't know about RestKit, but it seems that the 4 calls to featureMapping are each inserting a new RKAttributeMapping - that does not seem to make any sense. Maybe this is the reason you have a problem with your featureType property.
Related
Edit 2
For reasons that I dont quite understand, adding the response descriptor directly to httpsRKManager, instead of the app layering, got RK to recognize the "Response" response descriptor. The issue now is that it seems not to recognize the attribute mapping for "ErrorStatus" /end edit
I have three issues. First Shops and the recursive object Shop do not show up as part of the LLSResult object. Second the objects are not populated from the result, and third, is there a way to skip Shops altogether. The context is I am migrating an existing app from a Ruby server to a .NET server with a different api. To compound matters. two weeks ago I had never touched a Mac. let alone any of the ecosystem.
Edit 2:
The Response Descriptor fix
NSString *path = [[ConfigManager sharedInstance] getEventsURL];
[[[AppCore sharedInstance] httpsRKManager]addResponseDescriptor:
[RKResponseDescriptor responseDescriptorWithMapping:[LLSResponse jsonMapping]
method:RKRequestMethodAny
pathPattern:path
keyPath:#"Response"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
// This didn work
//[BaseRO addResponseDescriptorForPathPattern: [[ConfigManager sharedInstance] getEventsURL]
// withMapping:[LLSResponse jsonMapping]];
The log for edit 2:
} to object with object mapping (null)
2015-12-11 10:19:24.805 The Clymb[5234:149794] D restkit.object_mapping:RKPropertyInspector.m:131 Cached property inspection for Class 'LLSResponse': {
debugDescription = {
isPrimitive = 0;
keyValueCodingClass = NSString;
name = debugDescription;
};
description = {
isPrimitive = 0;
keyValueCodingClass = NSString;
name = description;
};
events = {
isPrimitive = 0;
keyValueCodingClass = Events;
name = events;
};
hash = {
isPrimitive = 1;
keyValueCodingClass = NSNumber;
name = hash;
};
shops = {
isPrimitive = 0;
keyValueCodingClass = Shops;
name = shops;
};
status = {
isPrimitive = 0;
keyValueCodingClass = ErrorStatus;
name = status;
};
}
2015-12-11 10:19:24.805 The Clymb[5234:149797] D restkit.object_mapping:RKMappingOperation.m:592 Mapping one to one relationship value at keyPath 'ErrorStatus' to 'ErrorStatus'
2015-12-11 10:19:24.806 The Clymb[5234:149797] T restkit.object_mapping:RKMappingOperation.m:550 Performing nested object mapping using mapping ErrorStatus> for data: {
"#ID" = "";
"#Status" = OK;
}
2015-12-11 10:19:24.806 The Clymb[5234:149797] D restkit.object_mapping:RKMappingOperation.m:868 Starting mapping operation...
2015-12-11 10:19:24.807 The Clymb[5234:149797] T restkit.object_mapping:RKMappingOperation.m:869 Performing mapping operation: for 'ErrorStatus' object. Mapping values from object {
"#ID" = "";
"#Status" = OK;
} to object with object mapping (null)
end of edit 2
The JSON
{
Response: {
ErrorStatus: {
#ID: "",
#Status: "OK"
},
Events: {
Event: [
{
#Title: "Test - Hero 1",
#ID: "00010033005800000000",
#Start: "2015-12-07 09:00:00Z",
#End: "2015-12-31 08:00:00Z",
#Status: "Active",
#Image_Small: "http://www.leftlanesports.com/App_Themes/Default/graphics/Events/291_00010033005800000000.jpg",
#Image_Large: "http://www.leftlanesports.com/App_Themes/Default/graphics/Events/447_00010033005800000000.jpg",
#TypeCode: "EVTH1",
Description: {
#cdata-section: "yo"
},
ShortDescription: {
#cdata-section: "50%##Hero Event 1"
}
},
...
]
},
Shops: {
Shop: [
{
#Title: "Adventures",
#ID: "00030000000000000000"
},
{
#Title: "Apparel",
#ID: "00080000000000000000",
Shop: [
{
#Title: "Mens",
#ID: "00080001000000000000",
Shop: [
{
#Title: "Accessories",
#ID: "00080001003700000000",
Shop: [
The .h file
#import
#import "BaseRO.h"
#import "EventDO.h"
#class LLSResponse;
#class Events;
#class ErrorStatus;
#interface LLSResponse : NSObject
#property (nonatomic, strong) ErrorStatus * status;
#property (nonatomic, strong) Events * events;
- getAllEvents;
#end
#interface ErrorStatus : NSObject
#property (nonatomic, copy) NSString * _id;
#property (nonatomic, copy) NSString * Status;
#end
#interface Events : NSObject ;
#property (nonatomic, strong) NSMutableArray *events;
#end
#interface Shop : NSObject ;
#property (nonatomic, copy) NSString * _id;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, strong) NSMutableArray *shops;
#end
#interface Shops : NSObject ;
#property (nonatomic, strong) NSMutableArray * shops;
#end
The relevant parts after edit 2 seem to be Response.jsonMapping and ErrorStatus.jsonMapping.
The .m file
#implementation LLSResponse
+ (RKObjectMapping *)jsonMapping
{
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace); //??? debugging
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[LLSResponse class]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"ErrorStatus"
toKeyPath:#"ErrorStatus"
withMapping:[ErrorStatus jsonMapping]]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Shops"
toKeyPath:#"Shops"
withMapping:[Shops jsonMapping]]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Events"
toKeyPath:#"Events"
withMapping:[Events jsonMapping]]];
return entityMapping;
}
- (NSArray *) getAllEvents
{
Events * events = self.events;
NSArray *allEvents = events.events;
return allEvents;
}
#end
#implementation ErrorStatus
+ (RKObjectMapping *)jsonMapping
{
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[ErrorStatus class]];
[entityMapping addAttributeMappingsFromDictionary:#{
#"#ID" : #"_id",
#"#Status" : #"Status"
}];
return entityMapping;
}
#end
#implementation Events
+ (RKObjectMapping *)jsonMapping
{
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[Events class]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Event"
toKeyPath:#"Event"
withMapping:[EventDO jsonMapping]]];
return entityMapping;
}
#end
#implementation Shop
+ (RKObjectMapping *)jsonMapping
{
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[Shop class]];
[entityMapping addAttributeMappingsFromDictionary:#{
#"#ID" : #"_id",
#"#Title" : #"title"
}];
//RKEntityMapping * shopMapping = [RKEntityMapping mappingForClass: [Shop class]];
//[entityMapping addRelationshipMappingWithSourceKeyPath:#"Shop" mapping:entityMapping];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Shop"
toKeyPath:#"Shop"
withMapping:entityMapping]];
return entityMapping;
}
#end
#implementation Shops
+ (RKObjectMapping *)jsonMapping
{
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[Shops class]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Shop"
toKeyPath:#"Shop"
withMapping:[Shop jsonMapping]]];
return entityMapping;
}
#end
When I run the app in the Xcode simulator I find that it has successfully called the server and the above JSON has been returned. The LLSResponse object, the ErrorStatus Object, and the Events object have been created. However the Shops object has not. So there is a problem in the Shops/Shop mapping, but I cant see it. When I examine the objects none of them have been populated. I dont know whether this is a separate issue or a consequence of the Shops problem.
Shops is actually extraneous data returned by the API. Is there a way to skip it? What happens if it is not mapped at all; is it an error?
EDIT 2: Text deleted.
Thanks
I am having some problems using an RKValueTransformer to serialize out an NSData image bytes to a base64 encoded string for a request. I was able to do the inverse for a response, after some help I received on stackoverflow.
Here is my code for creating the NSString to NSData value transformer, which works without issue. I found the index of the null value transformer and set it at afterNullTransformerIndex. I have also set it at index 0, but then I have to do my own null checking and this seems to work without issue.
//add the base64 to NSData transformer after the null value transformer
RKBlockValueTransformer *base64StringToNSDataTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class inputValueClass, __unsafe_unretained Class outputValueClass) {
return [inputValueClass isSubclassOfClass:[NSString class]] && [outputValueClass isSubclassOfClass:[NSData class]];
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputClass, NSError *__autoreleasing *error) {
RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSString class], error);
RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputClass, [NSData class], error);
*outputValue = [[NSData alloc] initWithBase64EncodedString:(NSString *)inputValue options:NSDataBase64DecodingIgnoreUnknownCharacters];
return YES;
}];
base64StringToNSDataTransformer.name = #"base64StringToNSDataTransformer";
[[RKValueTransformer defaultValueTransformer] insertValueTransformer:base64StringToNSDataTransformer atIndex:afterNullTransformerIndex];
And this is my code for creating the NSData to NSString value transformer, which isn't working. I set a breakpoint in the transformationBlock: method, but it never gets invoked.:
//add the NSData to String transformer for requests after the null value transformer
RKBlockValueTransformer *nsDataToBase64StringTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class inputValueClass, __unsafe_unretained Class outputValueClass) {
return [inputValueClass isSubclassOfClass:[NSData class]] && [outputValueClass isSubclassOfClass:[NSString class]];
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputClass, NSError *__autoreleasing *error) {
RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSData class], error);
RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputClass, [NSString class], error);
*outputValue = [((NSData *)inputValue) base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];
return YES;
}];
nsDataToBase64StringTransformer.name = #"nsDataToBase64StringTransformer";
[[RKValueTransformer defaultValueTransformer] insertValueTransformer:nsDataToBase64StringTransformer atIndex:afterNullTransformerIndex];
Like I said, my breakpoint never gets invoked in the transformationBlock: method, but the valueTransformationWithValidationBlock: does get invoked once when serializing the request, but only when transforming from a Date to a String. Looking through the stack in the debugger and RestKit's code, I found this method in RKObjectParameterization.m:
- (void)mappingOperation:(RKMappingOperation *)operation didSetValue:(id)value forKeyPath:(NSString *)keyPath usingMapping:(RKAttributeMapping *)mapping
{
id transformedValue = nil;
if ([value isKindOfClass:[NSDate class]]) {
[mapping.objectMapping.valueTransformer transformValue:value toValue:&transformedValue ofClass:[NSString class] error:nil];
} else if ([value isKindOfClass:[NSDecimalNumber class]]) {
// Precision numbers are serialized as strings to work around Javascript notation limits
transformedValue = [(NSDecimalNumber *)value stringValue];
} else if ([value isKindOfClass:[NSSet class]]) {
// NSSets are not natively serializable, so let's just turn it into an NSArray
transformedValue = [value allObjects];
} else if ([value isKindOfClass:[NSOrderedSet class]]) {
// NSOrderedSets are not natively serializable, so let's just turn it into an NSArray
transformedValue = [value array];
} else if (value == nil) {
// Serialize nil values as null
transformedValue = [NSNull null];
} else {
Class propertyClass = RKPropertyInspectorGetClassForPropertyAtKeyPathOfObject(mapping.sourceKeyPath, operation.sourceObject);
if ([propertyClass isSubclassOfClass:NSClassFromString(#"__NSCFBoolean")] || [propertyClass isSubclassOfClass:NSClassFromString(#"NSCFBoolean")]) {
transformedValue = #([value boolValue]);
}
}
if (transformedValue) {
RKLogDebug(#"Serialized %# value at keyPath to %# (%#)", NSStringFromClass([value class]), NSStringFromClass([transformedValue class]), value);
[operation.destinationObject setValue:transformedValue forKeyPath:keyPath];
}
}
It only appears that RestKit is using value transformers when value is an NSDate! Is there something that I am missing to get value transformers to work on requests?
EDIT answering Wain's questions and giving more details
This is my entity mapping code for responses. A record entity holds a collection of WTSImages:
RKEntityMapping *imageMapping = [RKEntityMapping mappingForEntityForName:#"WTSImage" inManagedObjectStore:self.managedObjectStore];
[imageMapping addAttributeMappingsFromDictionary:#{
#"id": #"dbId",
#"status": #"status",
#"type": #"type",
#"format": #"format",
#"width": #"width",
#"height": #"height",
#"image": #"imageData"
}];
imageMapping.identificationAttributes = #[#"dbId"];
[recordMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"images" toKeyPath:#"images" withMapping:imageMapping]];
The WTSImage class is generated from CoreData and looks like this:
#interface WTSImage : NSManagedObject
#property (nonatomic, retain) NSNumber * dbId;
#property (nonatomic, retain) NSString * format;
#property (nonatomic, retain) NSNumber * height;
#property (nonatomic, retain) NSData * imageData;
#property (nonatomic, retain) NSString * status;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSNumber * width;
#property (nonatomic, retain) WTSCaptureDevice *captureDevice;
#property (nonatomic, retain) WTSRecord *record;
#property (nonatomic, retain) WTSTempImageSet *tempImageSet;
#end
I create a reverse record mapping and add a request descriptor.
RKEntityMapping *reverseRecordMapping = [recordMapping inverseMapping];
[self addRequestDescriptor:[RKRequestDescriptor requestDescriptorWithMapping:reverseRecordMapping objectClass:[WTSRecord class] rootKeyPath:#"records" method:RKRequestMethodAny]];
This is the debug log output for mapping my image object to JSON. The imageData element does not look like a normal base64 encoded string:
2014-04-10 11:02:39.537 Identify[945:60b] T restkit.object_mapping:RKMappingOperation.m:682 Mapped relationship object from keyPath 'images' to 'images'. Value: (
{
format = JPEG;
height = 200;
id = 0;
image = <ffd8ffe0 00104a46 49460001 01000001 00010000 ffe10058 45786966 ... f77d7bf9 77b58fff d9>;
status = C;
type = MUGSHOT;
width = 200;
})
And here is the POST, which my server rejects:
2014-04-10 11:27:53.852 Identify[985:60b] T restkit.network:RKObjectRequestOperation.m:148 POST 'http://10.0.0.35:8080/Service/bs/records':
request.headers={
Accept = "application/json";
"Accept-Language" = "en;q=1, es;q=0.9, fr;q=0.8, de;q=0.7, ja;q=0.6, nl;q=0.5";
"Content-Type" = "application/x-www-form-urlencoded; charset=utf-8";
"User-Agent" = "Identify/1.0 (iPhone; iOS 7.1; Scale/2.00)";
}request.body=records[application]=Identify&records[createBy]=welcomed&records[createDt]=2014-04-10T15%3A27%3A42Z&records[description]&records[externalId]&records[groupId]=5&records[id]=0&records[images][][format]=JPEG&records[images][][height]=200&records[images][][id]=0&records[images][][image]=%3Cffd8ffe0%2000104a46%2049460001%2001000001%20000.......d773%20ffd9%3E&records[images][][status]=C&records[images][][type]=MUGSHOT&records[images][][width]=200&records[locked]&records[modifyBy]&records[modifyDt]&records[priv]
I had exactly the same issue - NSData of image content <-> NSString BASE64 encoded for the Rest call. I got the outbound working pretty quickly as you did, but the incoming mapping was a little trickier.
I raised this issue: https://github.com/RestKit/RestKit/issues/1949 and through some working through the problem, discovered that you need to set the propertyValueClass on the RKPropertyMapping in order to get RestKit to recognize that you want to turn the NSData into a NSString. Once this is done, you get the mapping done for you.
In RKObjectMapping classForKeyPath:, it is unable to find the class for my 'image' property. It appears that the _objectClass is a NSMutableDictionary rather than a WTSImage. This is causing the method to return a nil propertyClass
That makes sense, because the mapping destination for a request is NSMutableDictionary (and the source object is WTSImage. So, it doesn't apply any specific transformations and falls through to mappingOperation:didSetValue:forKeyPath:usingMapping: which you have already seen doesn't cater for this situation.
I think this will be hard to deal with using a transformer.
The only way I can think to deal with it right now is to add a method to WTSImage, say base64Image which returns the transformed image data and use that in your mapping (which means you won't be able to use [recordMapping inverseMapping]).
After a deep debug at [self transformValue:value toValue:&transformedValue withPropertyMapping:attributeMapping error:&error], I find the valueTransformer I added is not at the head but the second of all valueTransformer in defaultValueTransformer.
The first valueTransformer is a RKISO8601DateFormatter, by search this key word, I find it is inserted to the head of defaultValueTransformer in [RKObjectMapping initialize].
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Add an ISO8601DateFormatter to the transformation stack for backwards compatibility
RKISO8601DateFormatter *dateFormatter = [RKISO8601DateFormatter defaultISO8601DateFormatter];
[[RKValueTransformer defaultValueTransformer] insertValueTransformer:dateFormatter atIndex:0];
});
}
The problem is that my valueTransformer is inserted before the [RKObjectMapping initialize] method get called. In another word, RKISO8601DateFormatter is inserted to the head after mine which made my valueTransformer the second one.
My solution is quite simple, just call [RKObjectMapping new] before my insert code.
BTW, you should always give a name to your valueTransformer thus it could be quickly recognized when debug.
I have a JSON response as follows:
{
response : {
data : {
subjects : [
{
},
{
}
]
}
}
}
and i have a NSManagedObject as follows:
#interface Department : NSManagedObject
#property (nonatomic, retain) NSNumber * id;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSSet *subjects;
#end
I have a department record in Department table with id = 1 and name = "Physics". I want to modify this existing department with obtained subjects from response JSON data. the subjects field only to be updated rest should be kept same. Would you please tell me the RestKit 0.20 mapping code for this?
UPDATE
this is the mapping i performed:
NSDictionary *subMappingDict = #{
#"id": #"id",
#"sub_name": #"name"
};
RKEntityMapping *subMapping = [RKEntityMapping mappingForEntityForName:#"Subject" inManagedObjectStore:managedObjectStore];
[subMapping addAttributeMappingsFromDictionary:subMappingDict];
subMapping.identificationAttributes = #[#"id"];
RKEntityMapping *deptMapping = [RKEntityMapping mappingForEntityForName:#"Department" inManagedObjectStore:managedObjectStore];
[deptMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"subjects" toKeyPath:#"subjects" withMapping:subMapping]];
RKEntityMapping *collegeMapping = [RKEntityMapping mappingForEntityForName:#"College" inManagedObjectStore:managedObjectStore];
[collegeMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"data" toKeyPath:#"department" withMapping:deptMapping]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:collegeMapping pathPattern:nil keyPath:#"response" statusCodes:statusCodes];
I have an existing College record which has an existing Department record. Here i want to update Department's subjects field only by the mapping.
I am using RestKit 0.20 and have a problem with mapping a JSON with hierarchy which contains dynamic keys into one Object.
The JSON looks like this:
{
"id": 42,
"name": "Name of this entity",
"specialDataMap": {
"2091:10": {
"id": 2091,
"type": "10",
"value": "1'509.49",
"name": "Name of special data type 10"
},
"2091:02" {
"id": 2091,
"type": "02",
"value": "5.5543",
"name": "Name of special data type 02"
}
}
}
and should be mapped with RestKit to such an object:
#interface InfoPoint : NSManagedObject
#property (nonatomic, retrain) NSString* identifier;
#property (nonatomic, retrain) NSString* name;
#property (nonatomic, retrain) NSString* valueOfType10;
#property (nonatomic, retrain) NSString* valueOfType02;
#end
As you can see, I do not want to create a relationship and store the special data into a separate object. It just doesn't make sense.
I want to assign the nested attributes into the InfoPoint object like all other attributes. Usually this would work with the key path of the nested objects but this path contains a dynamic part: "2091:10" is a combination of the id and the type where the id might change (was not my 'original' idea but I have to consume it).
I have read about the Handling Dynamic Nesting Attributes in the RestKit documentation. But I did not find if and how this might work together with nested attributes.
----- added as response to comment/question: ----
I have tried it as well with Dynamic Object Mapping. But it did not work because RestKit seems to have problem with the #"self" destination:
RKEntityMapping* type10Mapping = [RKEntityMapping mappingForEntityForName:#"InfoPoint" inManagedObjectStore:objectStore];
[type10Mapping addAttributeMappingsFromDictionary:#{
#"value": #"valueOfType10"}];
RKDynamicMapping* dynamicMapping = [[RKDynamicMapping alloc] init];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"specialDataMap" toKeyPath:#"self" withMapping:dynamicMapping]];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKeyPath:#"type"] isEqualToString:#"10"]) {
return type10Mapping;
}
return nil;
}];
Your attempt at dynamic mapping isn't correct. You should supply the dynamic mapping to the response descriptor. The dynamic mapping should then create and return the appropriate mapping based on the keys it finds in the response. Something like (written free hand):
RKDynamicMapping* dynamicMapping = [[RKDynamicMapping alloc] init];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
RKEntityMapping* typeMapping = [RKEntityMapping mappingForEntityForName:#"InfoPoint" inManagedObjectStore:objectStore];
[typeMapping addAttributeMappingsFromDictionary:#{
#"id" : #"identifier",
#"name": #"name",}];
NSDictionary *types = [representation valueForKeyPath:#"specialDataMap"];
for (NSString *key in types) {
NSDictionary *type = [[types objectForKey:key] objectForKey:#"type"];
if ([type isEqualToString:#"10"]) {
[typeMapping addAttributeMappingsFromDictionary:#{
[NSString stringWithFormat:#"specialDataMap.%#.value", key]: #"valueOfType10"}];
} else if ...
}
return typeMapping;
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping
pathPattern:...
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
please help so here is the problem i got a rails app that sends a json image data base64. The json sends but when i try to access the data value in ios app it comes back as null. Here is my rails code,code
{
"profile_pic": "<%= Base64.encode64(File.read('/Users/rui_y/connect_Me2/public'+
#user.avatar_photo_url(:thumb)).gsub("\n", '')) %>",
}
the json comes back as
[{"profile_pic": "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH......
....... many more lines etc etc...................................................
+p/Ob9Xrf6D8po/hOhI4U5SSm4Es8vUE/riYmJj2B0iefbkz/2Q==" }]
the Restkit is mapped like this
[userMapping mapKeyPath:#"profile_pic" toAttribute:#"profilePic"];
NSLog(#"people id %#",[[objects objectAtIndex:0]profilePic ]);
//and it comes back as null
2012-02-09 20:08:48.073 ConnecTest[78232:207] profile_pic (null)
all the other values when i nslog is accessable so im wondering how to map this data.
You can map this as you do for all strings.
I take this in my code, but I map a list of key/value object :
Mapping :
listMapping = [RKObjectMapping mappingForClass:[VOKeyValue class]];
[listMapping mapKeyPath:#"value" toAttribute:#"value"];
[listMapping mapKeyPath:#"key" toAttribute:#"key"];
[[RKObjectManager sharedManager].mappingProvider setMapping:listMapping forKeyPath:#"list"];
Value Object :
#interface VOKeyValue : NSObject
{
NSString * key;
NSString * value;
}
#property (nonatomic, retain) NSString * key;
#property (nonatomic, retain) NSString * value;
#end
Request :
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"yourResourceURL..." delegate:self];
In my CallBack function :
VOKeyValue*img=[objects objectAtIndex:0];
if (![img.key isEqualToString:#"image"]) {
img=[objects objectAtIndex:1];
}
Finally I can map this Json :
{"list":[{"key":"image","value":"/9j/4AATSkZJRgABAgIAAAAAAAAAAAD/wAARCADwAUADASEAAhEBAxEB/....YFJCPAP/ZAAA="},{"key":"other","value":"xxxx..."}]}
I hope my exemple can help you.