NSJSONSerialization crashing app - ios

I have a dictionary that when I log it shows...
{
Date = "2013-04-30 17:17:18 +0000";
Description = Kb;
EventID = "92193e58-c04a-4233-9a6c-1332bc056b20";
Title = Keyboard;
}
I'm trying to turn it into NSData for a JSON web service like this...
- (NSData *)JSONRepresentation
{
NSDictionary *dictionary = [self dictionaryObject];
NSError *jsonError;
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:dictionary
options:0
error:&jsonError]; //This is where the error occurs.
return JSONData;
}
But every time I run it the app just crashes.
The dictionary is formed properly, the app just crashes at this line.
In AppCode I get the crash report...
EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT, subcode=0xdefe))
In Xcode the app just stops and if I try to continue it stops with an error...
EXC_BAD_ACCESS (code=1, address=0x0)

Your keys are invalid objects for converting to JSON. From the docs:
An object that may be converted to JSON must have the following
properties:
The top level object is an NSArray or NSDictionary. All objects are
instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull. All
dictionary keys are instances of NSString. Numbers are not NaN or
infinity.

check before, if the dictionary is invalid to convert, return.:
if (![NSJSONSerialization isValidJSONObject:dictionary]) { return; }

Related

Unarchiving data with new NSKeyedUnarchiver APIs while failed to satisfied unarchivedObjectOfClass it required

Firstly, I wanted to thank every of you have read the message and giving me feedback in any form.
Here it's what I'm trying to do. To replace deprecated API [NSKeyedUnarchiver unarchiveObjectWithData:myProfileData]; in objective-c to iOS 12 new API unarchivedObject<DecodedObjectType>(ofClass: DecodedObjectType.Type, from: Data) -> DecodedObjectType?
I unarchived data with old NSKeyedUnarchived APIs, printed it out and here is what I get:
assets = {
"albums" = {
"more_Album" = 0;
photos = (
);
};
"shared_connections" = "<null>";
}
My first attempt was the blow, because I knew I archived data as dictionary, so I must unarchive it with the same class type:
NSError *error = nil;
NSDictionary *dictionary = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSDictionary class] fromData:myData error:&error];
However the error message showed:
Error Domain=NSCocoaErrorDomain Code=4864 "value for key 'NS.objects' was of unexpected class 'NSNull (0x11c39fe80).
Allowed classes are '{(
"NSDictionary (0x11c39fa48)
)}
I thought maybe it's because I didn't include [NSNull class] in unarchivedObjectOfClass which caused the failure. I continued to make a set of unarchivedObject of classes in order to satisfy the complain:
NSError *error = nil;
NSSet *classSet = [NSSet setWithObjects:[NSDictionary class], [NSNull class], nil];
NSDictionary *dictionary = [NSKeyedUnarchiver unarchivedObjectOfClasses:classSet fromData:myData error:&error];
Unfortunately, it failed again.
Error Domain=NSCocoaErrorDomain Code=4864 "value for key 'NS.objects' was of unexpected class 'NSArray (0x11e79f778)
Allowed classes are '{(
"NSDictionary (0x11e79fa48) ,
"NSNull (0x11e79fe80) )}'
I am not sure what is the right way to handle those Null value in dictionary while using new NSKeyedUnarchiver APIs.
I got this all wrong and I realized I didn't include NSArray class in the NSSet and this is where complain came from.

NSJSONSerialization isValidJSONObject returns false for received data from Venue search endpoint

Xcode 8.1 Deployment target iOS 9.0
I'm getting an array of compact venue objects as expected from Foursquare Venue Search endpoint in...
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
When I check the data object using...
if ([NSJSONSerialization isValidJSONObject:data])
i get a false.
Can someone tell me what is wrong over here?
Edit:
Here is the complete if block (after adding typecast to data in if block)...
id foundationObject;
NSLog(#"data:- %#",data);
if ([NSJSONSerialization isValidJSONObject:(id)data])
{
foundationObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(#"venues foundation object:- %#",foundationObject);
}
Earlier the code didn't have the if block. just...
id foundationObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
The change was made when I realized (using breakpoint just after the above statement) that foundationObject was nil even though data wasn't.
Note: this worked fine earlier when I shipped my app for iOS 9.x in march. Could the version of the Venue Endpoint being called be making a difference?
What you're testing here is for NSData. The input for isValidJSONObject is id not NSData
+ (BOOL)isValidJSONObject:(id)obj;
It returns YES if obj can be converted to JSON data (NSData), otherwise NO.
Also, according to documentation,
An object that may be converted to JSON must have the following properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
Calling isValidJSONObject: or attempting a conversion are the definitive ways to tell if a given object can be converted to JSON data.
For converting NSData to JSONObject, you can use the following code
NSError *error;
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (!error) {
// successfully done.
}else {
NSLog(#"error: %#", error.localizedDescription)
}
Please note that to find out what's wrong with jsonData(NSData) you're receiving from the server, you have to pass NSError object into the method as shown in the above code. If conversion of NSData into jsonObject fails, you can find out why according to that.
Please look in to this link for more information on using NSError objects in Objective-C
You are using a wrong method here isValidJSONObject will tell you whether JSON object (id) will be converted to JSON data or not.
As per the doc
Returns a Boolean value that indicates whether a given object can be
converted to JSON data.
YES if obj can be converted to JSON data, otherwise NO.
If you want to check Data then you should use JSONObjectWithData:options:error: and check if it is nil or not.
Edit
You need to first convert your Data to NSDictionary or NSArray like this
NSMutableDictionary * dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
then check if dict is a valid son or not like this
if([NSJSONSerialization isValidJSONObject:dict]){
NSLog(#"dict is a valid JSON");
}else{
NSLog(#"dict is not valid JSON");
}

Get a single specific value from json

As you might expect, i'm fairly new to obj-C, and i'm constantly trying to build knowledge and experience. But i'm still struggling with a lot of concepts, and that includes JSON data 'catching'.
I've seen many tutorials and guides but i just can't translate them into what i need. Most of the time they layout data in arrays or get multiple values, and (of course) use different variables, which makes everything confusing and unclear to me, even though this should be stupidly simple.
I'm trying to do something very simple :
Get a single value from the open weather API, the temperature.
I'll show you my code which, according to my disgraceful knowledge, should be perfect, but apparently it doesn't work :D
#implementation HomeViewController
{
NSMutableArray *tableData;
NSDictionary *jsonDict;
NSMutableString *title;
}
-(void) viewDidLoad
{
[super viewDidLoad];
NSError *error;
//I create my data array and the string i'll store my value later on
tableData = [[NSMutableArray alloc] init];
title = [[NSMutableString alloc]init];
// Creating the link for the json api so it fits coordinates ; this works but i edited the locations out to clear the code
NSString *s = [[NSString alloc]initWithFormat:#"http://api.openweathermap.org/data/2.5/weather?lat=%.05f&lon=%.05f", _annotation.coordinate.latitude, _annotation.coordinate.longitude];
// I go online and catch the data of the url stored in S
NSData *jSonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:s]];
// This is a dictionary where all my data is stored from jsonData, keys and values all the way
jsonDict = [NSJSONSerialization JSONObjectWithData:jSonData options:NSJSONReadingMutableContainers error:&error];
// I use the string created previously and assign it the value stored in that dictionary, in the TEMP 'folder', right under MAIN.
title = [[jsonDict objectForKey:#"main"]objectForKey:#"main.temp"];
// I assign that title to a label so it appears in my view.
self.tempLabel.text = title;
...
}
There you go. I'm probably missing something very simple but i've been stuck on this and even if i feel I know what i'm doing, i'm probably missing something. So it'd be great if with the answer you give me, you could also tell me what I did wrong :D
Thank you very much for your support and knowledge. This community is amazing :)
Put a breakpoint after assigning value to jsonDict and use
po jsonDict
in the console to print out what you are getting. Then, adjust the code that extracts the value. And use modern Objective-C syntax for it.
Example
title = jsonDict[#"main"][#"temp"];
Note
po is a debugger command that will print out the contents of an object. If you need to print out the contents of a primitive, use p instead.
My guess is
jsonDict = [NSJSONSerialization JSONObjectWithData:jSonData options:NSJSONReadingMutableContainers error:&error];
is trying to create an nsdictionary, but it the results are coming back as an array. Try this:
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: jsonDict options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
This should set you right:
title = [[jsonDict objectForKey:#"main"]objectForKey:#"temp"];
To explain the issue, it seems you were referring to temp using a combination of dot syntax in the key.
EDIT: In response to your error:
That error appears when you're trying to find the length of a string on a value that is not of NSString type. Looks like temp is being returned as a number. So, to do what it looks like you're trying to do, you'll wanna convert [[jsonDict objectForKey:#"main"]objectForKey:#"temp"] to an NSString:
NSNumber *temp = [[jsonDict objectForKey:#"main"]objectForKey:#"temp"];
NSString *tempString = [temp stringValue];
OR
NSString *temp = [[[jsonDict objectForKey:#"main"]objectForKey:#"temp"] stringValue];
That will allow you to get the length: temp.length
**EDIT: Unless you're trying to get the length of the array of weather data...in which case i'd like to see more of that code

NSJSONSerialization with an integer

I'm writing an iOS application that gets data from a web request. The request returns the following:
{"hash":"0369a5d5e65335309b2b1502dc96b5aba691b9451c83b9","error":0}
I get the data from the NSData* responseData object as follows:
NSDictionary* JSONdata = [NSJSONSerialization JSONObjectWithData:_responseData options:0 error:&error];
NSInteger responseError = (NSInteger)[JSONdata objectForKey:#"error"];
However, responseError is coming back uninitialized (filled with garbage values). I tried changing NSInteger to NSString* but that yields the following error
'NSInvalidArgumentException', reason: '-[__NSCFNumber length]: unrecognized selector sent to instance 0x9d02220'
Any ideas?
You need to use an NSNumber. Primitive values will not work for as iOS parses the data in NSObjects: NSArray, NSDictionary, NSNumber, NSString, and NSNull.
Try this: NSNumber *responseError = [JSONdata objectForKey:#"error"];
Then to convert to an NSInteger you can do this:
NSInteger responseInt = [responseError integerValue]; this will return an integer value.
Also for future reference, an NSDictionary cannot contain any primitive values.

iOS JSON twitter response, convert to mutable

I have an app that is requesting a users list of followers. I would like to be able to change some of the data inside of the array I am getting back from twitter, but I can't seem to get it to become a proper mutable copy.
Here is my code:
NSMutableDictionary *theData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
NSMutableDictionary *TWData = [[NSMutableDictionary alloc] init];
TWData = [theData mutableCopy];
NSMutableArray *data = [TWData objectForKey:#"users"];
The order this is in and doing a mutable copy FIRST is the last thing I tried. This is the code that throws an error:
[[data objectAtIndex:2] setObject:#"indeed" forKey:#"following"];
And here is the typical error message:
[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
I understand WHY it is giving me the error, my question is how can I make EVERYTHING (all child dictionaries and array, and all of their child dictionaries and array, etc.) mutable so I can alter the data.
Any help is great, thanks!
You can use CFPropertyListCreateDeepCopy from Core Foundation:
NSMutableDictionary* mutableData = (NSMutableDictionary*) CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef) theData, kCFPropertyListMutableContainers));
See the CFPropertyList reference for more info. In particular, the Property List Mutability Options can be used to control what is mutable in the returned copy.

Resources