I have this issue when I'm trying to parse my jsonresult that comes from a webapi. Take note, that whenver it would return a json result that looks like this:
[{"Id":0, "Name":Wombat, "Category":Animal},
{"Id":1, "Name":Trident, "Category":Object}]
This code works on that result:
NSArray *jsonresult = [WCFServiceRequest processWebApiGETRequestWithURL:url];
if(jsonresult){
for(id item in jsonresult){
NSLog(#"item is %#", item);
if([item isKindOfClass:[NSDictionary class]]){
object.Id = [item objectForKey:#"Id"];
object.name = [item objectForKey:#"Name"];
object.category = [item objectForKey:#"Category"];
}
}
}
But once it returns a none list result that looks like so:
{"Id":1, "Name":Trident, "Category":Object}
It would not go through the
if([item isKindOfClass:[NSDictionary class]]){
Now if I take it out and just let it go straight to the assignment of the properties. The "item" variable that is returned from the jsonarray is the key eg: Id, Name, etc. Now I'm not sure how to properly iterate through the thing and assign it using keys. It seems like it's using indexes? Do I make another dictionary?
Its quite simple to get the solution of it, when you are unaware about the result DataType simply TypeCast your object to id (A Generic DataType)
Store your json Response in id
id jsonresult = [WCFServiceRequest processWebApiGETRequestWithURL:url];
// Check whether Response is An Array of Dictionary
if([jsonresult isKindOfClass:[NSArray class]])
{
NSLog(#"it has multiple Dictionary so iterate through list");
if(jsonresult){
for(id item in jsonresult){
NSLog(#"item is %#", item);
if([item isKindOfClass:[NSDictionary class]]){
object.Id = jsonresult[#"Id"];
object.name = jsonresult[#"Name"];
object.category = jsonresult[#"Category"];
}
}
}
}
// Its Dictionary
else if([jsonresult isKindOfClass:[NSDictionary class]])
{
NSLog(#"It has only one dictionary so simply read it");
object.Id = jsonresult[#"Id"];
object.name = jsonresult[#"Name"];
object.category = jsonresult[#"Category"];
}
Your response is Dictionary when you have only 1 Record in Result, and when you have more then 1 Record it will be an Array.
Related
Everything works when i get more then 1 objects back but when its only 1 it reacts weird, i can't find the solution for it.
First i set everything in an array:
NSArray *array = [[[dictionary objectForKey:#"Response"] objectForKey:#"objecten"] objectForKey:#"object"];
if (array == nil) {
NSLog(#"Expected 'results' array");
return;
}
then i use a for loop on a dictionary
for (NSDictionary *resultDict in array) {
SearchResult *searchResult;
NSString *wrapperType = [resultDict objectForKey:#"type"];
if ([wrapperType isEqualToString:#"rent"])
{
searchResult = [self parseHuur:resultDict];
}
if (searchResult != nil) {
[searchResults addObject:searchResult];
}}
So when results get back more then 1 everything works great, but when just one gets back i get:
-[__NSCFString objectForKey:]: unrecognized selector sent to instance 0x6e52c30
*** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSCFString objectForKey:]:
unrecognized selector sent to instance 0x6e52c30'
it points to this line:
NSString *wrapperType = [resultDict objectForKey:#"type"];
I really don't get it...
i check the results of the api in the browser with the same link and it really returns 1 object, but when i log the resultDict (NSlog it) i get only one answer: id instead of the whole object with all parameters (i don't know if this is the right name for it)
how can that be ?
Some of your results aren't full NSDictionaries but rather just NSStrings. You can check for this:
for (id result in array) {
if ([result isKindOfClass:[NSDictionary class]]) {
NSDictionary *resultDict = (NSDictionary *)result;
...
As per your comments, array is not always an array as you have mentioned. It could be an array or dictionary. So try this,
id someObject = [[[dictionary objectForKey:#"Response"] objectForKey:#"objecten"] objectForKey:#"object"]; //naming it as someObject since it is not always an array
if (someObject == nil) {
NSLog(#"Expected 'results' array");
return;
}
if ([someObject isKindOfClass:[NSDictionary class]]) { //Just add this
someObject = [NSArray arrayWithObject:someObject];
}
NSArray *array = (NSArray *)someObject;//type cast to an array now
for (NSDictionary *resultDict in array) {
SearchResult *searchResult;
NSString *wrapperType = [resultDict objectForKey:#"type"];
if ([wrapperType isEqualToString:#"rent"])
{
searchResult = [self parseHuur:resultDict];
}
if (searchResult != nil) {
[searchResults addObject:searchResult];
}
}
When you use the fast enumeration for a NSDictionary, the iterating variable is from the set of keys in the dictionary not the values.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocFastEnumeration.html
The resultDict isn't an NSDictionary hence you can't invoke objectForKey: on this object.
A better solution would be to treat resultDict as id type in the for loop, and check its class type for NSDictionary before using it.
-[__NSCFString objectForKey:]
So it's calling the objectForKey: method on an NSString. It seems that the API you're using for getting the objects follows a common idiom: it uses duck-typing/polimorphism (to use these nice OO-related words) and it returns either an array of objects if it has more than results, or a single object (and not an array of one element) when it has only one result. So, you have to use reflection (OMG, even more OO terminology!) to inspect whether the returned object is actually an array - either
id result = [dictionary objectForKey:#"Response"];
id value;
if ([result isKindOfClass:[NSDictionary class]]) {
value = [[result object objectForKey:#"objecten"] objectForKey:#"object"];
} else {
value = result;
}
or
id value;
if ([dictionary isKindOfClass:[NSDictionary class]]) {
value = [[[dictionary objectForKey:#"Result"] result object objectForKey:#"objecten"] objectForKey:#"object"];
} else {
value = dictionary;
}
Try both, whichever works should be fine.
I have a few NSManagedObject classes. I am pulling down some JSON data from a server which I parse into an NSDictionary object. When the conversion from JSON to NSDictionary occurs, all of my data is cast as NSStrings. When I then map this dictionary to my managedObject I get this:
Unacceptable type of value for attribute: property = "idexpert"; desired type = NSNumber; given type = __NSCFString; value = 1.'
So my managedobject is looking for an NSNumber but it's getting a string and throwing an exception
Is there a way that when I call setValuesForKeysWithDictionary I can automagically cast the values properly for the managedobject they are going into?
Thanks!
The best way to manage JSON attributes while saving in core data is to write a generic function that can override setValuesForKeysWithDictionary as per below:
#implementation NSManagedObject (safeSetValuesKeysWithDictionary)
- (void)safeSetValuesForKeysWithDictionary:(NSDictionary *)keyedValues dateFormatter:(NSDateFormatter *)dateFormatter
{
NSDictionary *attributes = [[self entity] attributesByName];
for (NSString *attribute in attributes) {
id value = [keyedValues objectForKey:attribute];
if (value == nil) {
continue;
}
NSAttributeType attributeType = [[attributes objectForKey:attribute] attributeType];
if ((attributeType == NSStringAttributeType) && ([value isKindOfClass:[NSNumber class]])) {
value = [value stringValue];
} else if (((attributeType == NSInteger16AttributeType) || (attributeType == NSInteger32AttributeType) || (attributeType == NSInteger64AttributeType) || (attributeType == NSBooleanAttributeType)) && ([value isKindOfClass:[NSString class]])) {
value = [NSNumber numberWithInteger:[value integerValue]];
} else if ((attributeType == NSFloatAttributeType) && ([value isKindOfClass:[NSString class]])) {
value = [NSNumber numberWithDouble:[value doubleValue]];
} else if ((attributeType == NSDateAttributeType) && ([value isKindOfClass:[NSString class]]) && (dateFormatter != nil)) {
value = [dateFormatter dateFromString:value];
}
[self setValue:value forKey:attribute];
}
}
#end
For more details refer this link here: http://www.cimgf.com/2011/06/02/saving-json-to-core-data/
If the json you are receiving actually has number values and they are getting cast as string you should get a new json parser. I recommend NXJson. Otherwise there wont be any magical casting happening.
If the json is returning strings such as {"idexpert":"1"} then you can override setValuesForKeysWithDictionary and do something like the code below;
-(void)setValuesForKeysWithDictionary:(NSDictionary *)d{
NSMutableDictionary *newDict = [NSMutableDictionary dictionaryWithDictionary:d];
NSString *value = [newDict valueForKey:#"idexpert"];
[newDict setValue:[NSNumber numberWithLong:[value longValue]] forKey:#"idexpert"];
[super setValuesForKeysWithDictionary:newDict];
}
I have a method which returns me a nsdictionary with certain keys and values. i need to change the key names from the dictionary to a new key name but the values need to be same for that key,but i am stuck here.need help
This method will only work with a mutable dictionary. It doesn't check what should be done if the new key already exists.
You can get a mutable dictionary of a immutable by calling mutableCopy on it.
- (void)exchangeKey:(NSString *)aKey withKey:(NSString *)aNewKey inMutableDictionary:(NSMutableDictionary *)aDict
{
if (![aKey isEqualToString:aNewKey]) {
id objectToPreserve = [aDict objectForKey:aKey];
[aDict setObject:objectToPreserve forKey:aNewKey];
[aDict removeObjectForKey:aKey];
}
}
You can't change anything in an NSDictionary, since it is read only.
How about loop through the dictionary and create a new NSMutableDictionary with the new key names ?
Could you not add a new key-value pair using the old value, and then remove the old key-value pair?
This would only work on an NSMutableDictionary. NSDictionarys are not designed to be changed once they have been created.
To change specific key to new key, I have written a recursive method for Category Class.
- (NSMutableDictionary*)replaceKeyName:(NSString *)old_key with:(NSString )new_key {
NSMutableDictionary dict = [NSMutableDictionary dictionaryWithDictionary: self];
NSMutableArray *keys = [[dict allKeys] mutableCopy];
for (NSString key in keys) {
if ([key isEqualToString:old_key]) {
id val = [self objectForKey:key];
[dict removeObjectForKey:key];
[dict setValue:val forKey:new_key];
return dict;
} else {
const id object = [dict objectForKey: key];
if ([object isKindOfClass:[NSDictionary class]]) {
[dict setObject:[dict replaceKeyName:old_key with:new_key] forKey:key];
} else if ([object isKindOfClass:[NSArray class]]){
if (object && [(NSArray)object count] > 0) {
NSMutableArray *arr_temp = [[NSMutableArray alloc] init];
for (NSDictionary *temp_dict in object) {
NSDictionary *temp = [temp_dict replaceKeyName:old_key with:new_key];
[arr_temp addObject:temp];
}
[dict setValue:arr_temp forKey:key];
}
}
}
}
return dict;
}
I'm trying to parse a very simple json object with 1 value. (ocUnit test below)
- (void) testHatCartParseWithValidRefId {
NSString* data = #"{\"refid\":999}";
Cart* obj = [HatCartParseJson parseJsonAndReturnObject:data];
STAssertTrue([obj.refid isEqualToString:#"999"], #"fail");
}
In the implementation everything fails when I add the line to pull it from either key or index. How should I pull this from the json input? Please keep in mind I need this json parse (not string) the actual code I'm working with is a large set of JSON data.
+ (Cart *) parseJsonAndReturnObject:(NSString *)json
{
NSArray* cart = [json JSONValue];
for (NSDictionary* item in cart) {
Cart* obj = [[Cart alloc] init];
//NSString* refid = [item objectAtIndex:0];
//NSString* refid = [item objectForKey:#"refid"];
[obj setRefid:#"999"];
return obj;
}
return nil;
}
Thank you in advance
You are expecting that the return value of JSONValue is an NSArray, which in this case it isn't.
So, you must do a check if the return value is actually an NSArray, and if it is, then iterate through the collection, otherwise check if it's an NSDictionary, and if it is, then return the Cart object with the refid from the NSDictionary. If all of this fails, then just return nil.
As a side point, according to Apple's Object Ownership Policy, you should return autorelease-d objects from methods whose names do not contain the words "alloc", "new" or "copy". This would be one such method where you'd return an autorelease-d object.
+ (Cart *) parseJsonAndReturnObject:(NSString *)json
{
id cart = [json JSONValue];
NSString* refid = nil;
if([cart isKindOfClass:[NSArray class]]) {
refid = [[cart objectAtIndex:0] objectForKey:#"refid"];
} else if([cart isKindOfClass:[NSDictionary class]]) {
refid = [cart objectForKey:#"refid"];
}
if(refid) {
Cart* c = [[Cart alloc] init];
[c setRefid:refid];
return [c autorelease];
}
return nil;
}
I'm having a hard time scraping together enough snippets of knowledge to implement an NSOutlineView with a static, never-changing structure defined in an NSArray. This link has been great, but it's not helping me grasp submenus. I'm thinking they're just nested NSArrays, but I have no clear idea.
Let's say we have an NSArray inside an NSArray, defined as
NSArray *subarray = [[NSArray alloc] initWithObjects:#"2.1", #"2.2", #"2.3", #"2.4", #"2.5", nil];
NSArray *ovStructure = [[NSArray alloc] initWithObjects:#"1", subarray, #"3", nil];
The text is defined in outlineView:objectValueForTableColumn:byItem:.
- (id)outlineView:(NSOutlineView *)ov objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)ovItem
{
if ([[[tableColumn headerCell] stringValue] compare:#"Key"] == NSOrderedSame)
{
// Return the key for this item. First, get the parent array or dictionary.
// If the parent is nil, then that must be root, so we'll get the root
// dictionary.
id parentObject = [ov parentForItem:ovItem] ? [ov parentForItem:ovItem] : ovStructure;
if ([parentObject isKindOfClass:[NSArray class]])
{
// Arrays don't have keys (usually), so we have to use a name
// based on the index of the object.
NSLog([NSString stringWithFormat:#"%#", ovItem]);
//return [NSString stringWithFormat:#"Item %d", [parentObject indexOfObject:ovItem]];
return (NSString *) [ovStructure objectAtIndex:[ovStructure indexOfObject:ovItem]];
}
}
else
{
// Return the value for the key. If this is a string, just return that.
if ([ovItem isKindOfClass:[NSString class]])
{
return ovItem;
}
else if ([ovItem isKindOfClass:[NSDictionary class]])
{
return [NSString stringWithFormat:#"%d items", [ovItem count]];
}
else if ([ovItem isKindOfClass:[NSArray class]])
{
return [NSString stringWithFormat:#"%d items", [ovItem count]];
}
}
return nil;
}
The result is '1', '(' (expandable), and '3'. NSLog shows the array starting with '(', hence the second item. Expanding it causes a crash due to going 'beyond bounds.' I tried using parentForItem: but couldn't figure out what to compare the result to.
What am I missing?
The example behind the link you included shows an NSDictionary taking care of the subarray stuff, if I'm reading it correctly. So I think your ovStructure should not be an array but a dictionary. But, more fundamentally, I think you should really look into NSTreeController. Unfortunately, NSTreeController is notoriously hard to work with, but improvements were made last year and even I got it working in the end. Good luck.