So let's say I have a JSON object like such:
{
"userList" : [
{
"ID" : 1,
"firstName" : "John",
"lastName" : "Doe"
},
{
"ID" : 2,
"firstName" : "Jane",
"lastName" : "Doe"
}
]
}
I am able to map this object into my user class which have the following attribute:
ID,
firstName,
lastName,
createdDate,
modifiedData
The Problem arise when I am need to update modified date I want to be able to insert a data-time stamp whenever I do a mapping along with when I modified the data while in offline mode.
So my question is, how do I map JSON object to Core Data while also inserting some data that is not present in the JSON object. Is this even possible?
================
My Mapping Function, if it helps:
+ (RKObjectMapping *)mapping {
// Create a singleton instance of the mapping object.
__strong static RKEntityMapping *_mapping = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RKManagedObjectStore *store = [[RKObjectManager sharedManager] managedObjectStore];
_mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:store];
// Map attributes with the same name in the JSON and the model.
[_mapping addAttributeMappingsFromDictionary:#{#"ID": #"ID",
#"firstName" : #"firstName",
#"lastName" : #"lastName"}];
// Set primaryKeyAttribute
_mapping.identificationAttributes = #[#"ID"];
});
return _mapping;
}
Handle this outside RestKit, but in a way that is triggered by RestKit (and any other change):
Override willSave on your managed object subclass and update the modified date whenever it's called (setting the primitive value to avoid recursion).
Related
I need to have one property which is not present in JSON but it need to be set when I try to use this Model Object. I think it'd be easier to show you the example...
Here is my sample JSON:
[{
"name" : "Safari",
"key" : "safari",
"app_url_scheme" : "http://www.twitter.com",
"actions" :
[{
"key" : "show_profile",
"url_format" : "http://www.twitter.com/{{profile_screenname}}"
}]
}
]
And JSONKeyPathsByPropertyKey method looks like this:
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"appName" : #"name",
#"appKey" : #"key",
#"appURLScheme" : #"app_url_scheme",
#"appActions" : #"actions",
#"isInstalled" : NSNull.null
};
}
So, as you can see there is this isInstalled method which should be ignored during mapping (as there isn't field like this in JSON) but I need to set this property as soon as this Object is fully mapped. What is more I need to set it based on other property provided in JSON. How to achieve this?
I've found solution like this:
#"videoType" : #"#Selector(videoTypeFromString:, type)",
//! implemented on instance you are parsing
- (NSUInteger)videoTypeFromString:(NSString *)type
{
if ([type isEqualToString:#"shortVideo"]) {
return VideoTypeShort;
}
return VideoTypeLong;
}
But this #selector is never being called...
How can I use Github Mantle to choose a property class based on another property in the same class? (or in the worse case another part of the JSON object).
For example if i have an object like this:
{
"content": {"mention_text": "some text"},
"created_at": 1411750819000,
"id": 600,
"type": "mention"
}
I want to make a transformer like this:
+(NSValueTransformer *)contentJSONTransformer {
return [MTLValueTransformer transformerWithBlock:^id(NSDictionary* contentDict) {
return [MTLJSONAdapter modelOfClass:ETMentionActivityContent.class fromJSONDictionary:contentDict error:nil];
}];
}
But the dictionary passed to the transformer only includes the 'content' piece of the JSON, so I don't have access to the 'type' field. Is there anyway to access the rest of the object? Or somehow base the model class of 'content' on the 'type'?
I have previously been forced to do hack solutions like this:
+(NSValueTransformer *)contentJSONTransformer {
return [MTLValueTransformer transformerWithBlock:^id(NSDictionary* contentDict) {
if (contentDict[#"mention_text"]) {
return [MTLJSONAdapter modelOfClass:ETMentionActivityContent.class fromJSONDictionary:contentDict error:nil];
} else {
return [MTLJSONAdapter modelOfClass:ETActivityContent.class fromJSONDictionary:contentDict error:nil];
}
}];
}
You can pass the type information by modifying the JSONKeyPathsByPropertyKey method:
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
NSStringFromSelector(#selector(content)) : #[ #"type", #"content" ],
};
}
Then in contentJSONTransformer, you can access the "type" property:
+ (NSValueTransformer *)contentJSONTransformer
{
return [MTLValueTransformer ...
...
NSString *type = value[#"type"];
id content = value[#"content"];
];
}
I've had a similar issue, and I suspect my solution isn't much better than yours.
I have a common base class for my Mantle objects, and after each is constructed, I call a configure method to give them chance to set up properties that are dependent on more than one "base" (== JSON) property.
Like this:
+(id)entityWithDictionary:(NSDictionary*)dictionary {
NSError* error = nil;
Class derivedClass = [self classWithDictionary:dictionary];
NSAssert(derivedClass,#"entityWithDictionary failed to make derivedClass");
HVEntity* entity = [MTLJSONAdapter modelOfClass:derivedClass fromJSONDictionary:dictionary error:&error];
NSAssert(entity,#"entityWithDictionary failed to make object");
entity.raw = dictionary;
[entity configureWithDictionary:dictionary]; // Give the new entity a chance to set up derived properties
return entity;
}
My question is very similar to RestKit: mapping JSON array of strings, except it is specific to RestKit 0.09. I have looked at several other questions but am still stuck.
For reasons beyond my control I cannot upgrade to RestKit 0.20. The JSON I'm working with is:
{
"brands":[
"AN",
"UO",
"FP",
"TR",
"BN"
],
"meta":{
"message":"Current Registry Configuration."
},
"event_types":[
"WEDDING",
"ANNIVERSARY",
"BIRTHDAY",
"HOUSEWARMING",
"SPECIAL OCCASION"
],
"links":[
]
}
I am able to map the "meta" (and several other domain) objects just fine. But I have not been able to map "event_types", and have no need for "brands" or "links".
My current code is as follows:
+ (void) addElementMappings:(RKObjectMapping *)mapping
{
if ([[self superclass] respondsToSelector:#selector(addElementMappings:)]) {
[[self superclass] performSelector:#selector(addElementMappings:)
withObject:mapping];
}
[mapping mapKeyPath:#"event_types"
toAttribute:#"eventType"];
RKObjectManager* objectManager = [RKObjectManager sharedManager];
if (![objectManager.mappingProvider mappingForKeyPath:#"event_types"]) {
[objectManager.mappingProvider setMapping:mapping
forKeyPath:#"event_types"]; // for KVC path
}
}
and eventType is a NSArray (I've also tried defining it as NSString - same results as below).
As is, the code throws an exception "this class is not key value coding-compliant for the key event_types."
If I change the mapKeyPath from #"event_types" to nil (similar to 0.20), I get an exception "Cannot define an element mapping an element name to map from".
If I omit the [mapping mapKeyPath:toAttribute] entirely, there are no exceptions but of course I only get the meta object, not event_types.
In RestKit 0.09, how do I map attributes without keys?
The solution was to create a class (GRConfigDTO) that contained the nested objects of interest (brands, event_types) as NSArray. The mapping was simple:
[mapping mapKeyPath:#"brands"
toAttribute:#"brands"];
[mapping mapKeyPath:#"event_types"
toAttribute:#"eventTypes"];
My predecessors had modified RestKit .09 (and our server JSON) to ensure that mapping was never ambiguous and therefore always used nil targetClass - which is why upgrading RestKit was not an option. I was working with a 3rd party service and had to deal with their JSON as-is.
The key to making it all work was specifying the targetClass in the sendObject call:
[self sendObject:nil
path:kConfig
requestType:GET
targetClass:[GRConfigDTO class]
delegate:self
callbackMethod:#selector(registryEventTypesResults:error:)];
With the targetClass set, RestKit was able to perform the mapping as expected.
I want to make myself things easier so I'm creating a dictionary that is used by other function (to reach key by object or object by key) but that dictionary is always static. Is this fine way to do it or I need property or something else?
+ (NSDictionary *)dictionaryWithCategoriesAndStrings
{
return #{
kNewsCategoryAll : #(NewsCategoryAll),
kNewsCategoryRadio : #(NewsCategoryRadio),
kNewsCategoryEconomics : #(NewsCategoryEconomics),
kNewsCategoryCulture : #(NewsCategoryCulture),
kNewsCategorySport : #(NewsCategorySport),
kNewsCategoryTravel : #(NewsCategoryTravel),
kNewsCategoryMusic : #(NewsCategoryMusic),
kNewsCategorySociety : #(NewsCategorySociety),
kNewsCategoryHealth : #(NewsCategoryHealth)
};
}
So now I always access this same dictionary through function [self dictionaryWithCategoriesAndString];
Note: Those are keys are static strings declared at top and objects are NSNumbers with integer.
Rather than exposing the static to the entire class, you could create it within the method, and initialise it only once with gcd:
This is thread safe.
+ (NSDictionary *)dictionaryWithCategoriesAndStrings {
static NSDictionary *dict;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dict = #{
kNewsCategoryAll : #(NewsCategoryAll),
kNewsCategoryRadio : #(NewsCategoryRadio),
kNewsCategoryEconomics : #(NewsCategoryEconomics),
kNewsCategoryCulture : #(NewsCategoryCulture),
kNewsCategorySport : #(NewsCategorySport),
kNewsCategoryTravel : #(NewsCategoryTravel),
kNewsCategoryMusic : #(NewsCategoryMusic),
kNewsCategorySociety : #(NewsCategorySociety),
kNewsCategoryHealth : #(NewsCategoryHealth)
};
});
return dict;
}
You will be creating a new NSDictionary every time you call this method, so you won't really be accessing the same dictionary as it will just be a new identical one each time. You won't be able to make a property either if you're using it statically. Maybe something a little more like this just so you are accessing the same dictionary each time.
To access this in an instance method, you could use [[self class] dictionaryWithCategoriesAndStrings].
static NSDictionary* dict;
+ (NSDictionary *)dictionaryWithCategoriesAndStrings
{
if(dict == nil)
{
dict = #{
kNewsCategoryAll : #(NewsCategoryAll),
kNewsCategoryRadio : #(NewsCategoryRadio),
kNewsCategoryEconomics : #(NewsCategoryEconomics),
kNewsCategoryCulture : #(NewsCategoryCulture),
kNewsCategorySport : #(NewsCategorySport),
kNewsCategoryTravel : #(NewsCategoryTravel),
kNewsCategoryMusic : #(NewsCategoryMusic),
kNewsCategorySociety : #(NewsCategorySociety),
kNewsCategoryHealth : #(NewsCategoryHealth)
};
}
return dict;
}
Almost right. Every time that selector is executed, a new NSDictionary is created. That is bad. It should only be created once, and it should only be created lazily.
#property (strong, nonatomic) NSDictionary *categoryDict;
- (NSDictionary *) categoryDict
{
if( !_categoryDict)
{
_categoryDict = #{ #"Key" :#"value" , ....};
}
return _categoryDict;
}
Now the dictionary is only created once. and you can get to the dictionary using dot notation.
I'm using RestKit for posting objects to the server. In my object I have two properties: name and socialId.
I want to send to server only properties that have data.
If name != nil send:
{
"name" : "name",
}
If socialId != 0 send:
{
"socialId" : socialId,
}
But RestKit sends all data. For example:
{
"name" : "",
"socialId" : 0,
}
How can I change this behavior?
The answer is to using [RKDynamicMapping setObjectMappingForRepresentationBlock:]
You should check representation properties in block and add attributes mapping for desired properties.