I have a CoreData model with 2 objects. The first object (Images) contains a list of image names. The second object (Book) has a relationship with a field from the Images object called imageId. The data type is set to int16 and is marked optional with a default value of 0, as not every book will have an image.
I request a JSON representation of the Book object from a WCF service. One of the book objects returned has "ImageId" : null as its value. So when converting the JSON object into a managed object in Objective-C I get the following error message:
Unacceptable type of value for attribute: property = "imageId"; desired type = NSNumber; given type = NSNull; value =
How do you handle this conversion? I thought of checking for null and then setting the value to 0 but this doesn't seem correct.
Many thanks.
*UPDATE *
In the process of implementing Daniels solution I discovered that other fields could also be returned from the service as null. Is it therefore possible to modify the NSDictionary category to enable it to traverse itself and replace any instance of with nil? I am using jsonKit as my JSON parser and it seems to return the parsed result as a NSMutableDictionary with NSDictionary objects representing my JSON objects. I have spent much of the day trying to modify my objects but as they are returned as NSDictionaries they are immutable. I could simply use Daniels solution on every field that needed checking for however if the service changed and another field was returned as then this would break my app. Alternatively I could implement Daniels solution on every field just incase it was ever but not sure if thats a good solution especially for performance.
Some JSON libraries let you set an option so that JSON nulls are simply omitted from the dictionary as opposed to using NSNull. If you do that, then it will work because [theDict objectForKey:#"imageID"] will return nil.
But other than that, the right thing to do is look out for NSNull explicitly. Making an NSDictionary category is a handy way to do it:
#implementation NSDictionary (DDNullGetter)
- (id)nonNullObjectForKey:(id)key
{
id val = [self objectForKey:key];
return (val == [NSNull null] ? nil : val);
}
#end
Related
I'm a nwebie in Core Data, i have designed a navigation based application and some of the data i use are created on run time(come from a URL via JSON). I took a few tutorials an searched for almost a day but haven't still realized how to save the incoming JSON data to the Entity (or event?) in my Core Data model. I fetch the data in the DetailViewController class and i need to save this data to Core Data(I have prepared an Entity with 7 properties). Can anyone please help?(If you know a good tutorial or sample code i will be pleased)
EDIT This may be a little specific but i really have trouble with and need just a little help.
My data comes to the app from a kind of restful server(i wrote it in PHP), firstly user enters his/her login informations(which i have saved to the database on server before) and when the response data comes i will use different elements of it in differen views(for example the user_id will be used on a view and the buttonData etc on other views). My question is, how will i save JSON data into my core data model(has tree Entities for the moment). Thanks in advance
Note: I lokked arround a lot but couldn't find any answer&tutorial about an app like mine
The best way to do that would be to create entities corresponding to JSON structure. Easiest was is when each JSON object becomes an entity, and arrays become arrays of entities. Be reasonable, however, and don't introduce too much overkill for JSON subobjects that are essentially part of its superobject.
When you have created entities, you can start off with the parsing and translation. Use some JSON framework (starting from iOS5 there's one from Apple) and parse JSON string into object tree, where root item is either an NSArray or NSDictionary, and subelements will be NSArray, NSDictionary, NSNumber, NSString or NSNull.
Go over them one by one in iterational loops and assign according values to your core data entity attributes. You can make use of NSKeyValueCoding here and avoid too much manual mapping of the attribute names. If your JSON attributes are of the same name as entity attributes, you'll be able to just go over all dictionary elements and parse them into attributes of the same name.
Example
My parsing code in the similar situation was as follows:
NSDictionary *parsedFeed = /* your way to get a dictionary */;
for (NSString *key in parsedFeed) {
id value = [parsedFeed objectForKey:key];
// Don't assign NSNull, it will break assignments to NSString, etc.
if (value && [value isKindOfClass:[NSNull class]])
value = nil;
#try {
[yourCreatedEntity setValue:value forKey:property];
} #catch (NSException *exception) {
// Exception means such attribute is not defined in the class or some other error.
}
}
This code will work in trivial situation, however, it may need to be expanded, depending on your needs:
With some kinds of custom mappings in case you want your JSON value be placed in differently named attribute.
If your JSON has sub-objects or arrays of sub-objects, you will need to detect those cases, for example in setters, and initiate new parsing one level deeper. Otherwise with my example you will face the situation that assigns NSDictionary object to an NSManagedObject.
I don't think it is reasonable to dive into these, more advanced matters in scope of this answer, as it will expand it too much.
I suggest you to use this library : https://github.com/TouchCode/TouchJSON
And then if you want to make a factory to parse json and feed your code data, you can use selectors to call methods to fill all your attributes.
Chances are your JSON data gets converted to an NSDictionary or NSArray (or some combination of the two). Simply extract the key/values from the JSON structure and add them to your entity class.
This lib helps me lot
Features
Attribute and relationship mapping to JSON key paths.
Value transformation using named NSValueTransformer objects.
Object graph preservation.
Support for entity inheritance
Works vice-versa
My initial code was
[[[show Episodes] lastObject] setDescription:nodeContent];
This gets the last object from the Episodes array and tries to set the episode's description property using nodeContent. This does not work because I think the compiler does not know the class of the episode object.
I then tried providing a cast
((EpisodeRepresentation *)[[show Episodes] lastObject]).description = nodeContent;
This doesn't work either. I have also tried unsuccessfully
EpisodeRepresentation *rep = [[show Episodes] lastObject];
rep.description = nodeContent;
All these ways leave description null (nodeContent is definitely not null, it is a NSString).
Your array is empty. Try [[show Episodes] count] to get the number of objects in the array.
BTW: Your naming of classes and methods seems odd. Classes usually begin with Captal letters and methods with lower case letters.
3 possibilities
array has no value make a check
show.episods doesnt have this object
nodecontent is null
description is an NSobject property which you are overriding here.Better use another property name for description
I'm relatively new to Objective-C (mainly using cocoa/apple foundation framework), but a long-time C++ programmer, so I'll start by explaining what I'm trying to accomplish; It's quite possible my approach isn't the best one, so I'd love to hear any suggestions for a different approach.
I've got a text file in json format, just made this quick example:
"section1" : {
"director" : "Sample Name 1",
"writers" : {
"name" : "Example Name 1",
"name" : "Example Name 2",
},
},
And I've got a class with properties which I want to match with the data inside this file, since I'm planning to parse this file and store some of the values in an instance of this class. My class header would look roughly like this:
#interface SongData : NSObject
#property (nonatomic, copy) NSString *director;
#property (nonatomic, copy) NSArray *writers;
#end
So I'm trying to find the cleanest, somewhat "data-driven" way to store this json data into the correct properties. Basically I want a way to loop through the json "dictionary" of data, and somehow use the "keys" to match with the correct property in my class and assign the value to that property. My first crack at it, I created a dictionary where the "key" is the actual key to lookup the proper data inside the json data, and the "value" would be a pointer to the correct class property. Sort of like this:
NSDictionary *descriptionDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
director, #"name",
writers, #"writers",
nil];
Then the idea was to loop through the json data (which is stored in another NSDictionary), use each key from my "descriptionDictionary" to lookup the appropriate value in the json data, then use the matching "value" of the description dictionary based on the same key (which I was hoping is sort of a pointer to the actual class property?) and set that properties value to the value from the json data. Perhaps an example will make it a bit more clear :-). Lets assume "jsonDictionary" is the result of parsing my json data, and I'm already inside section1, so there should be a 1-1 match between keys that make up the jsonData NSDictionary and the keys that make up my descriptionDictionary NSDictionary:
[descriptionDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
value = [jsonDictionary objectForKey:propertyName];
}];
Now "value" would technically be pointing to one of my classes properties, and I want to assign relevant json data to it (ie director = #"Sample Name 1"). I know there's problems with what I'm doing (I realize that I probably can't just dereference a pointer to a random class property and assign an arbitrary object of unknown type to it, hoping it all matches up :-) but hoping my example at least illustrates what I'm trying to do so someone can tell me a better way.
Have a look at KVC (source: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/BasicPrinciples.html)
Setting Attribute Values Using Key-Value Coding
The method setValue:forKey: sets the value of the specified key,
relative to the receiver, to the provided value. The default
implementation of setValue:forKey: automatically unwraps NSValue
objects that represent scalars and structs and assigns them to the
property. See “Scalar and Structure Support” for details on the
wrapping and unwrapping semantics.
If the specified key does not exist, the receiver is sent a
setValue:forUndefinedKey: message. The default implementation of
setValue:forUndefinedKey: raises an NSUndefinedKeyException; however,
subclasses can override this method to handle the request in a custom
manner.
The method setValue:forKeyPath: behaves in a similar fashion, but it
is able to handle a key path as well as a single key.
Finally, setValuesForKeysWithDictionary: sets the properties of the
receiver with the values in the specified dictionary, using the
dictionary keys to identify the properties. The default implementation
invokes setValue:forKey: for each key-value pair, substituting nil for
NSNull objects as required.
One additional issue that you should consider is what happens when an
attempt is made to set a non-object property to a nil value. In this
case, the receiver sends itself a setNilValueForKey: message. The
default implementation of setNilValueForKey: raises an
NSInvalidArgumentException. Your application can override this method
to substitute a default value or a marker value, and then invoke
setValue:forKey: with the new value.
As long as your class properties have the same names as the JSON fields you can use the setValuesForKeysWithDictionary: and pass in the JSON root dictionary.
For keys/properties that are named differently you can simply override the setValue:forUndefinedKey: and set the appropriate property yourself.
Finally there is the case of a value being represented by a different type in the JSON than in the property. For example NSURL would be an NSString instead. Here you can simply check the class of the passed parameter and if it does not match the IVAR, do a conversion.
I'm a nwebie in Core Data, i have designed a navigation based application and some of the data i use are created on run time(come from a URL via JSON). I took a few tutorials an searched for almost a day but haven't still realized how to save the incoming JSON data to the Entity (or event?) in my Core Data model. I fetch the data in the DetailViewController class and i need to save this data to Core Data(I have prepared an Entity with 7 properties). Can anyone please help?(If you know a good tutorial or sample code i will be pleased)
EDIT This may be a little specific but i really have trouble with and need just a little help.
My data comes to the app from a kind of restful server(i wrote it in PHP), firstly user enters his/her login informations(which i have saved to the database on server before) and when the response data comes i will use different elements of it in differen views(for example the user_id will be used on a view and the buttonData etc on other views). My question is, how will i save JSON data into my core data model(has tree Entities for the moment). Thanks in advance
Note: I lokked arround a lot but couldn't find any answer&tutorial about an app like mine
The best way to do that would be to create entities corresponding to JSON structure. Easiest was is when each JSON object becomes an entity, and arrays become arrays of entities. Be reasonable, however, and don't introduce too much overkill for JSON subobjects that are essentially part of its superobject.
When you have created entities, you can start off with the parsing and translation. Use some JSON framework (starting from iOS5 there's one from Apple) and parse JSON string into object tree, where root item is either an NSArray or NSDictionary, and subelements will be NSArray, NSDictionary, NSNumber, NSString or NSNull.
Go over them one by one in iterational loops and assign according values to your core data entity attributes. You can make use of NSKeyValueCoding here and avoid too much manual mapping of the attribute names. If your JSON attributes are of the same name as entity attributes, you'll be able to just go over all dictionary elements and parse them into attributes of the same name.
Example
My parsing code in the similar situation was as follows:
NSDictionary *parsedFeed = /* your way to get a dictionary */;
for (NSString *key in parsedFeed) {
id value = [parsedFeed objectForKey:key];
// Don't assign NSNull, it will break assignments to NSString, etc.
if (value && [value isKindOfClass:[NSNull class]])
value = nil;
#try {
[yourCreatedEntity setValue:value forKey:property];
} #catch (NSException *exception) {
// Exception means such attribute is not defined in the class or some other error.
}
}
This code will work in trivial situation, however, it may need to be expanded, depending on your needs:
With some kinds of custom mappings in case you want your JSON value be placed in differently named attribute.
If your JSON has sub-objects or arrays of sub-objects, you will need to detect those cases, for example in setters, and initiate new parsing one level deeper. Otherwise with my example you will face the situation that assigns NSDictionary object to an NSManagedObject.
I don't think it is reasonable to dive into these, more advanced matters in scope of this answer, as it will expand it too much.
I suggest you to use this library : https://github.com/TouchCode/TouchJSON
And then if you want to make a factory to parse json and feed your code data, you can use selectors to call methods to fill all your attributes.
Chances are your JSON data gets converted to an NSDictionary or NSArray (or some combination of the two). Simply extract the key/values from the JSON structure and add them to your entity class.
This lib helps me lot
Features
Attribute and relationship mapping to JSON key paths.
Value transformation using named NSValueTransformer objects.
Object graph preservation.
Support for entity inheritance
Works vice-versa
In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.