Converting a Dictionary with a value of [NSNull null] to JSON - ios

I am trying to use this code:
NSDictionary *innerMessage
= #{#"nonce":[NSNumber numberWithInteger:nonce],
#"payload":#{#"login": [NSNull null]}};
NSError * err;
NSData * innerMessageData = [NSJSONSerialization
dataWithJSONObject:innerMessage options:0
error:&err];
to create a JSON object with the following structure:
{
"apikey": "e418f5b4a15608b78185540ef583b9fc",
"signature": "FN6/9dnMfLh3wZj+cAFr82HcSvmwuniMQqUlRxSQ9WxRqFpYrjY2xlvDzLC5+qSZAHts8R7KR7HbjiI3SzVxHg==",
"message":{
"nonce": 12,
"payload": {
"Login": {}
}
}
}
However, this is the actual result which I get:
{"nonce":1398350092512,"payload":{"login":null}}
Why is [NSNull null] in my dictionary not being converted to {}?
How would I need to change my code to get the correct JSON
structure?
Thank you!

NSNull is suppose to become null in the JSON.
If you want an empty dictionary in the JSON ({}) you should use an empty dictionary - #{}.

Change code to
NSDictionary *innerMessage = #{
#"nonce":#12,
#"payload":#{#"login": #{}}
};
NSError * err;
NSData * innerMessageData = [NSJSONSerialization
dataWithJSONObject:innerMessage options:0
error:&err];
This will create the desired response
{
nonce = 12,
payload = {
login = {}
},
}

null is a perfectly valid JSON value; what were you expecting? How should NSJSONSerialization know that you wanted [NSNull null] to be converted to an empty object? (For that matter, why wouldn’t nulls be converted to an empty array, an empty list, or numeric zero?)
The solution is to process your innerMessage before you serialize it, replacing any instances of [NSNull null] with #{} (or, equivalently, [NSDictionary dictionary]).

Related

iOS JSON Parsing, Format Issues

So I was working on a project that required me to work with some JSON, I was running into a few issues regarding the best way of representing things. First of, this is how the JSON looks:
"phoneList": [
{
"phoneReason": "End of Contract",
"phoneType": [
{
"id": 5,
"phoneType": "Android Smartphone"
}
]
}
]
I want to know the most appropriate way of representing this.
For example, I do know that that my phoneReason will just be a simple NSString while my phoneType is actually a NSArray. However,I wasn't sure how to represent a)the id, I know this is an integer, but should this be an NSInteger or an NSNumber and b)could someone point me in the direction of some sample code where I can understand how to model a dictionary object containing an integer and a string and also where I can understand how to model an array of dictionaries.
My other question is also similar in that say I'm actually posting something, how do I model this, specifically say for like dictionary type (JSON Curly Brace)objects that contain a number/integer and a string.
For example, this is the JSON I'm trying to model and then do something like this:
"phoneReason": "Upgrade",
"phoneInfo": {
"id": "2"
},
//And then I want to pass ID
-(void) createOurRequest:(NSNumber *)id {
NSDictionary *myDictionary = #{
#"phoneReason" : [NSString stringWithFormat:#"%i", s elf.dat.reason],
//How do I then represent the phoneInfo element exactly?
};
Sorry, for the clumsy question, would really appreciate any guidance on modeling JSON in iOS or just generally.
I'm assuming you're asking questions a) and b), and also how to model a JSON.
a) The unfortunate thing with Obj-C is that all collection elements have to be objects. Integers are value types, so they will need to be converted to NSNumbers to work. However, if you're parsing a JSON string, the builtin JSON parser does it for you. I'll describe it below.
b) The model is based on the JSON. You describe the object collection and the parser will determine the model for you. In your example, you would have a NSDictionary<NSString *: NSArray<NSDictionary<NSString *: id>*>*>. The innermost element has value of id because you can either have an NSString ("End of Contract") or an NSArray ("phoneType": [ { "id": 5, "phoneType": "Android Smartphone" } ])
Of course, the model is defined by your JSON, so if you run it through a parser, you get a structured object. You can access each element based on your model (object[#"phoneList"][#"phoneReason"]).
The class method to use is:
+ (id)JSONObjectWithData:(NSData *)data
options:(NSJSONReadingOptions)opt
error:(NSError **)error
Where you pass it a NSData representation of your string, options (or 0), and a NSError pointer (error*). You get back a parsed JSON with the proper structure you defined.
NSDictionary *parsedJSONObject = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:NULL
I have no options to use and I know there will be no error, so I pass nothing for those parameters. The resulting object will be in whatever structure your JSON is.
Using the objects and the json layout you provided in your first example, this is how I would go about creating the dictionaries and arrays to get the json in the format you specified. Hopefully this helps make it a little clearer for you.
// example constructor method
-(void) jsonStringWithPhoneReason:(NSString*)reason phoneId:(NSInteger)phoneId phoneType:(NSString*)phoneType
{
// create device detail dictionary
NSDictionary *deviceOneDetail = #{
#"id" : #(phoneId), // <- set phone id as NSNumber
#"phoneType" : phoneType // <- set your string phone type
};
// create device dictionary
NSDictionary *deviceOne = #{
#"phoneReason" : reason, // <- set your phone reason string
#"phoneType" : #[deviceOneDetail] // <- set your phone type dictionary within an array
};
// create phone list dictionary with any device dictionaries you want to add
NSDictionary *phoneListDict = #{
#"phoneList" : #[
deviceOne, // <- add your device to the phone list array of dictionaries
// deviceTwo...
]
};
NSString *jsonString = [self convertToJsonString:phoneListDict]; // <- convert the dictionary into a json string and use however you wish
// your json string should now look like this assuming you pass 'End of Contract', 5 & 'Android Smartphone' as arguments to this method
// {"phoneList":[{"phoneReason":"End of Contract","phoneType":[{"id":5,"phoneType":"Android Smartphone"}]}]}
}
-(NSString*) convertToJsonString:(NSDictionary*)dictionary
{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:0 // Pass 0 if you don't care about the readability of the generated string
error:&error];
if (error)
{
NSString *errorDesc = [NSString stringWithFormat:#"Error creating json data from dictionary: %#", error.localizedDescription];
NSLog(#"ERROR: %#", errorDesc);
jsonData = nil;
return nil;
}
NSString *returnString = nil;
if(jsonData != nil)
{
returnString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
return returnString;
}

How to parse nested JSON objects?

I get the JSON format like this:
stream( { posts: [{CHANNEL: {ios: "(format=m3u8-aapl).m3u8"} }]})
What I want to get is an array for the "ios".
This is my code:
id jsonObjects = [NSJSONSerialization JSONObjectWithData:
jsonSource options:NSJSONReadingMutableContainers error:nil];
for (NSDictionary *dataDict in jsonObjects) {
NSArray *ios_data = [[[dataDict objectForKey:#"posts"] objectForKey:#"CHANNEL"] objectForKey:#"ios"];
NSLog(#"%#",ios_data);
dict = [NSDictionary dictionaryWithObjectsAndKeys:ios_data, ios,nil];
}
but it return in NULL, what the problem of it?
Your "JSON":
stream( { posts: [{CHANNEL: {ios: "(format=m3u8-aapl).m3u8"} }]})
Is not JSON. You can try running it through a validator like http://jsonlint.com/ to test it out.
Also, you should create an NSError reference to pass in instead of nil so NSJSONSerialization can vend you an error object. This will help in your debugging.
Here is an example of what your data would look like if it were valid JSON:
{
"stream": [
{
"posts": [
{
"CHANNEL": {
"ios": "(format=m3u8-aapl).m3u8"
}
}
]
}
]
}
(I spaced it out to be more legible, but the spacing is unnecessary for parsing.)
Once you have your array holding 'posts' you can drill down like this:
NSArray *fileData = [myDic1 valueForKeyPath:#"posts.CHANNEL"];
and then access like this:
for (int i=0; i < [clientFileData count]; i++) {
NSDictionary *myDictionary = [fileData objectAtIndex:i];
]
Also, unless I am wrong, your JSON file seems incorrectly formatted.
Get your response like:
NSDictionary *dictionaryData = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
Correct JSOn should be like this
{
"posts":[
{
"CHANNEL":{
"ios":"(format=m3u8-aapl).m3u8"
}
}
]
}
Get the correct format of JSON and then try to parse it. And one thing you were doing wrong is POST is an array.

How to get an integer value from NSDictionary?

I am having this weird issue. NSDictionary is not returning the correct integer value.
JSON response code from server.
{
"status":"ok",
"error_code":0,
"data" : [],
"msg":"everything is working!"
}
The JSON is being converted to a NSDictionary.
NSError *error = nil;
NSDictionary *jsonDict = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error: &error];
I access the NSDictionary value using the following code.
int error_code = (int)[jsonDict valueForKey:#"error_code"]
NSLog(#"%i", error_code);
The log outputs the following: 143005344
I've even tried objectForKey and I get the same response.
Thanks, in advance.
Yes it outputs the pointer of the value and that's why you see this log output.
You cannot cast the pointer to an integer and expect the value.
int error_code = [[jsonDict valueForKey:#"error_code"] integerValue];
or if you want to use modern objective-c
int error_code = [jsonDict[#"error_code"] integerValue];
Numbers are stored as NSNumber instances in plist/json dictionaries. Try calling one of NSNumber's getters like intValue:
int error_code = [jsonDict[#"error_code"] intValue];
Note also the new subscripting syntax for dictionaries.

How to process response from Instapaper API?

I make a request to the Instapaper API, and it's supposed to return JSON. It returns something close to JSON, but not completely, like follows:
2013-05-30 19:54:20.155 --[53078:c07] (
{
type = meta;
},
{
"subscription_is_active" = 1;
type = user;
"user_id" = --;
username = "--#gmail.com";
},
{
"bookmark_id" = 387838931;
description = "";
hash = YHwQuwhW;
"private_source" = "";
progress = 0;
"progress_timestamp" = 0;
starred = 0;
time = 1369954406;
title = "Adobe Finally Releases Kuler Color-Picking App for iPhone - Mac Rumors";
type = bookmark;
url = "http://www.macrumors.com/2013/05/30/adobe-finally-releases-kuler-color-picking-app-for-iphone/";
},
How do I then process this? Can I take it and turn it into an NSDictionary even though it doesn't seem to be valid JSON?
From Instapaper API Docs:
Instapaper strings are always encoded in UTF-8, and Instapaper expects all input to be in UTF-8.
Unless otherwise noted, output from every method is an array. The output array is returned as JSON by default.
You can specify a jsonp parameter with a callback function name, e.g. jsonp=myCallback, to use JSONP and wrap the output in a call to the specified function.
So there is no way you will get not valid JSON!
Try following code:
NSData *jsonData = [[NSString stringWithContentsOfURL:[NSURL urlWithString:#"http://your-instapeper-API-link"] encoding:NSUTF8StringEncoding error:nil] dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
id serializationJSON = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
And then you can log what is wrong or if result is what you expect:
NSLog(#"class of JSON input: %# \n and possible error: %#",[serializationJSON class],error);
Of course you should expect Array and no error.
EDIT ... based on coment code:
Based on docs you should get Array or Dictionary. Please add this core instead your line #23 (numer from here):
if([JSON isKindOfClass:[NSDictionary class]]) {
NSDictionary *jsonDictionary = JSON;
NSLog(#"%#",[jsonDictionary allKeys]);
} else {
NSLog(#"JSON object class: %#",[JSON class]);
}
and please show us output.
One more thing:
You get array from request. Great! This is a valid JSON. So you need to debug it. As i said it's a shame is not a unlimited acccess public API, so i can look into it. But now you have to debug your result. I see in your code that you are trying to access bookmarks. So i look into Bookmarks section in docs and this is some kind of list (NSArray). So if you don't know what result you want. You should print them into log (or set a breakpoint). Replace code from my earlier update with this simple log:
NSDictionary *resultDictionary;
if([JSON isKindOfClass:[NSArray class]]) {
NSArray *jsonArray = JSON;
NSLog(#"so json is an array with %i objects",[jsonArray count]);
for(id objectInsideArr in jsonArray) {
NSLog(#"object in array [class]: %# [value]: %#",[objectInsideArr class],objectInsideArr); //if here you find NSDictionary maybe is this dictionary you are looking for. I'm not sure what it is.
if([objectInsideArr isKindOfClass:[NSDictionary class]]) {
resultDictionary = [[NSDictionary alloc] initWithDictionary:objectInsideArr];
}
}
}
If it were me I would write a custom formatter to get it into JSON format and then use NSJSONSerialization once I know it is valid. What you posted is so far from valid there is no way it would work. I'm surprised they are returning it in that format, do they provide some kind of library for consuming their services?
If you want something even simpler, I can give you my CGIJSONObject library that will handle JSON using reflection - you just need to mirror the keys in APIs with your classes andit is good to go.

Putting JSON into an Array

I'm a noob when it comes to requests and JSON. Inside my app I send to the server and get back stuff so I can use it of course. I tried looking up different things but none really seem to be what I'm looking for. So I'm getting back what seems to be formatted JSON. What I want to know how to do is put it into a NSMutable array. The way I get this JSON is by using AFNetworking's AFJSONRequestOperation.
My response looks like this.
{
id = 38;
name = "St. Martin Hall";
},
{
id = 40;
name = "Assumptions Commons";
},
{
id = 41;
name = "Vickroy Hall";
},
{
id = 42;
name = "St. Ann Hall";
},
{
id = 37;
name = "Duquesne Towers";
}
if your JSON format like {"mainKey":[{},{},...]}
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* dataArray = [json objectForKey:#"mainKey"]; //2
else your JSON format like [{},{},...]
NSError* error;
NSArray* dataArray = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
I think your format is case 2: [] Array of Object {}
Tutorial: http://www.raywenderlich.com/5492/working-with-json-in-ios-5
JSON:http://www.json.org
You use the NSJSONSerialization class to convert JSON to Foundation objects and convert Foundation objects to JSON.
This class is available in iOS 5.0+. If you're targetting older iOS version, have a look at a third-party JSON framework:
Comparison of JSON Parser for Objective-C (JSON Framework, YAJL, TouchJSON, etc)
If that's what you're getting back it's not JSON I'm afraid. It does look like Javascript in a way but it should be more like
[
{
"id" : 38,
"name" : "St. Martin Hall"
},
{
"id" : 39,
"name" : "Assumptions Commons"
}
]

Resources