Is the following statement correct, or am I missing something?
You have to check the return object of NSJSONSerialization to see if it is a dictionary or an array - you can have
data = {"name":"joe", "age":"young"}
// NSJSONSerialization returns a dictionary
and
data = {{"name":"joe", "age":"young"},
{"name":"fred", "age":"not so young"}}
// returns an array
Each type has a different access method which breaks if used on the wrong one.
For example:
NSMutableArray *jsonObject = [json objectAtIndex:i];
// will break if json is a dictionary
so you have to do something like -
id jsonObjects = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
if ([jsonObjects isKindOfClass:[NSArray class]])
NSLog(#"yes we got an Array"); // cycle thru the array elements
else if ([jsonObjects isKindOfClass:[NSDictionary class]])
NSLog(#"yes we got an dictionary"); // cycle thru the dictionary elements
else
NSLog(#"neither array nor dictionary!");
I had a good look thru stack overflow and Apple documentation and other places and could not find any direct confirmation of the above.
If you are just asking if this is correct or not, yes it is the safe way to process jsonObjects. It's also how you would do it with other API that returns id.
Related
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");
}
I need to parse a json and get the key value pairs in the same sequence as they are present in response.
Currently what i'm doing is
-(instancetype)initWithJson:(NSDictionary *)responseDict {
if (self = [super init]){
NSArray *tempArray = [[NSArray alloc] init];
NSArray *roomSizesForAcArray = [responseDict valueFromDictionaryWithNSNullCheck:#"roomSizesForAc"];
NSArray *loadChartForInverterArray = [responseDict valueFromDictionaryWithNSNullCheck:#"loadChartForInverter"];
if(roomSizesForAcArray && roomSizesForAcArray.count>0){
self.isInverterChart=false;
tempArray=roomSizesForAcArray;
}
else if(loadChartForInverterArray && loadChartForInverterArray.count>0){
self.isInverterChart=true;
tempArray=loadChartForInverterArray;
}
self.arrayOfChartSizeObjects=tempArray;
if(tempArray && tempArray.count>0){
//Temp array first object is a dictionary which i need to parse sequentially
self.arrayOfKeys = [[tempArray objectAtIndex:0] allKeys];
//This array of keys is random every time and not sequential
}
}
return self;
}
I need to someway parse the dictionary [tempArray objectAtIndex:0] maintaining the order of keys in init.
Not clear what you want, you want dictionary while you are already parsing the dictionary. To get dictionary from the JSON use below code.
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:receiveData options:kNilOptions error:&error];
To get all the keys and then retrieve the details use
NSArray *sortedKeys = [[jsonDict allKeys]
Once you have keys then get the details
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
Dictionaries are unordered collections. You say "...maintaining the order of keys in init". There is no such thing.
JSON "Objects", the JSON equivalent of a NSDictionaries/Swift Dictionaries, have the same issue.
Some JSON libraries will preserve the order in which key/value pairs are submitted to be sent, but you should not depend on that. The JSON protocol does not guarantee it.
Once you receive JSON data and convert it to an NSDictionary/Dictionary, the order in which the key/value pairs is sent is lost. The only way I know of to preserve the (already unreliable) order of the key/value pairs from the original JSON data stream is to parse the JSON yourself rather than deserializing it using NSJSONSerialization.
If you want your data in a particular order, you should use an array as the container object to send it. You can send an array of key/value pairs if you need to.
I am a novice at IOS dev and i really need some help.
I want to parse (which is working) and fetch some JSON Data.
i used this tutorial for the http request and json parsing
http://www.mysamplecode.com/2013/04/ios-http-request-and-json-parsing.html
everything works fine with a 1 dimensional dictionary
but I need to be able to fetch the following JSON Data
[{"defaultGateway": "10.10.10.254", "hostname": "On", "connected": "true", "subnetMask": "255.255.255.255", "iPAddress": "10.10.10.10", "dhcpEnabled": "true"},
{"defaultGateway": "10.10.10.254", "hostname": "On", "connected": "true", "subnetMask": "255.255.255.255", "iPAddress": "10.10.10.10", "dhcpEnabled": "true"}]
after I use the following function I got the following dictionary which I really don't know how to access
NSDictionary * res = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
here is a picture of the dictionary
http://www11.pic-upload.de/09.11.14/5n4hdg3eh84q.png
How can I access for example the defaultGateway in the first dictionary?
You have a small logical error in your example. The expression [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; returns an NSArray and not and NSDictionary. So, first you should change this part to:
NSArray *result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
I know it's an array because your JSON string contains an array of two JSON objects. NSJSONSerialization will transform JSON arrays into objects of type NSArray and JSON objects into objects of type NSDictionary.
If you're not sure what your JSON contains, you can do the following:
id result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if ([result isKindOfClass:[NSArray class]]){
// do something with the array
}
else if ([result isKindOfClass:[NSDictionary class]]){
// do something with the dictionary
}
As for your question, you can access the data in an NSDictionary by providing a key, which you can do in two ways:
NSString *gateway = [dictionary objectForKey:#"defaultGateway"];
or even faster:
NSString *gateway = dictionary[#"defaultGateway"];
Coming back to your example, to access the defaultGateway from the first of your two JSON objects, you can do the following:
NSArray *result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; // parse the JSON and store in array
NSDictionary *dict = result[0]; // take the first of the two JSON objects and store it in a dictionary
NSString *gateway = dictionary[#"defaultGateway"]; // retrieve the defaultGateway property
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
I am writing an app which is supposed to read JSON data from a remote web service. the URL is in the format "something.action".
I use dataWithContentsofURL to download the JSOn data from the service and I see that this function does return back some data.
Using this data I call the function NSJSONSeraiization, but the response of this is null.
Basically, I am using the code provided on http://www.raywenderlich.com/5492/working-with-json-in-ios-5. The only difference is that I am fetching data from a remote server and my URL is of the form "something.action".
I am not able to figure out what is going wrong here.
Check a JSON viewer like http://jsonviewer.stack.hu/ to confirm if the JSON returned is correct or not.
Do an NSLog of the string obtained from the NSData you receive and use it with the viewer. If there are any issues, you know why the object is not serialized.
Check the response you get is a properly-formatted JSON file, if it is check its class:
NSError *jsonError = nil;
NSJSONSerialization *myJSONObject = [NSJSONSerialization JSONObjectWithData:theNSDataObained options:NSJSONReadingMutableContainers error:&jsonError];
if(jsonError != nil){
NSLog(#"JSON Error: %#", jsonError);
return;
}
//An NSJSONSerialization object will either be an NSDictionary or
//an NSArray, figure out what it is:
NSLog(#"JSON class is: %#", [myJSONObject class]);
//if it's a NSDictionary:
if([myJSONObject isKindOfClass:[NSDictionary class]]){
NSDictionary *myJSONDictionary = (NSDictionary *) myJSONObject;
for(id key in myJSONDictionary){
id value = [myJSONDictionary valueForKey:key];
[value doStuff];
}
}
else //if it's a NSArray....