Converting NSData (Json data) to nsarray : iOS - ios

I am getting some data from WebService (which is a json data)
Exact input sent by Webservice is:
[
{
"id": "C-62",
"title": "testing testing",
"type": "image",
"thumb": "",
"LinkId": "ABC",
"fileSize":1074,
"level":0,
},
{
"id": "C-63",
"title": "testing ab testing",
"type": "audio",
"thumb": "",
"LinkId": "ABCD",
"fileSize":1074,
"level":1,
}
]
As I can see in input data that only fileSize is a integer type Key-Value pair
remaining all are in string format.
When I convert that NSData onto NSArray using following code
NSArray *dataArray = nil;
dataArray = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
I get "dataArray" like this
{
"id": "C-62",
"title": "testing testing",
"type": image,
"thumb": "",
"LinkId": "ABC",
"fileSize":1074,
"level":0,
},
{
"id": "C-63",
"title": "testing ab testing",
"type": audio,
"thumb": "",
"LinkId": "ABCD",
"fileSize":1074,
"level":1,
}
The "type" Key-Value pair changes to "int" .All other key-value pairs are in same state.
Please help me in order to convert the input data into the same format as sent by web service.
I am using iOS 7.

You just got yourself confused. You confuse NSLog output with what is really stored. You are also not saying the truth when you claim that only the "fileSize" field contains numbers, because clearly the "level" field does as well.
NSLog only shows strings with quotes when necessary. A string "10" would be displayed as 10 without the quotes. You can really rely on NSJSONSerializer not doing anything behind your back.

NSJSONSerialization will convert scalar numbers to NSNumbers. That's the only way these can participate in collections (arrays and dictionaries). To convert to int, access the NSNumber and convert it as follows...
NSArray *dataArray = // the de-serialized json
NSDictionary *dictionary = dataArray[0]; // the first dictionary
NSNumber *number = dictionary[#"fileSize"];
// turn an NSNumber representing an integer to a plain int
int fileSizeInt = [number intValue];
But don't try to reinsert that int back into the dictionary.

The one tricky thing about JSON on iOS is figuring out the data type of an arbitrary NSNumber. I chronicled my difficulties in this question.
A large number might be floating-point or integer, and if you attempt to extract one when you really have the other you lose significant digits. And, obviously (in retrospect), if you extract a too-large value into an int you will lose high-order bits and get totally wrong values.
(This isn't quite so difficult if you know in advance that certain values are of certain types. Then it's just a matter of using the proper "verb" to extract the expected type of value.)

Related

Parsing JSON in Objective C with key names as incremented integers

not really sure how to even word this question BUT I have some JSON that I need to parse which is formatted like so:-
"nodes": {
"4": {
"node_id": 4,
"title": "TITLE 1",
"description": "",
},
"7": {
"node_id": 7,
"title": "TITLE 2",
"description": "",
},
"12": {
"node_id": 12,
"title": "TITLE 3",
"description": "",
},
Normally I would grab values with the standard [dictionary objectForKey#"key"] but as the items begin with an integer (string) I am finding it hard to parse correctly. Am I missing something really simple here?
If you just want to access one value than you would do the following.
// Assume JSONObject is the "root" dictionary
NSDictionary *fourDictionary = JSONObject[#"nodes"]["4"] // This will get the object from "4":{}
Now if the JSON in question can have a dynamic number of objects than this is much trickier. The easiest solution would be to get the provider of the JSON to rewrite it as an array of objects. You could than look up the object you want using the object's node_id value.
If this is not possible than you could attempt to write a for loop to loop through all of the keys in the "nodes" object and access the items from the dictionary that way.
i.e.
NSMutableArray *arrayOfNodes = [NSMutableArray array];
NSDictionary *nodes = JSONObject[#"nodes"];
for(NSString *numberKey in [nodes allKeys])
{
NSDictionary *nodeObject = nodes[numberKey]
[arrayOfNodes addObject:nodeObject];
}
// Do anything else with the nodes now that they are in an array.

iOS Game Center send json in match data

I am trying to send JSON data in matchData object when a user end its turn. If I check the json before sending it is valid and looks like,
JSON:
{
"p1score" : "0",
"turn" : "0",
"pb1" : "BPS1120|2231|3422|4213|5244|6135",
"player2" : "0000177110",
"player1" : "0000177110",
"p2score" : "0",
"movements" : "MVS2242",
"pb2" : "BPS1630|2511|3522|4543|5534|6625",
"moves" : "30"
}
Prepares the data for sending,
NSData *matchData = [[NSString stringWithFormat:#"%#",realMatchData] dataUsingEncoding:NSUTF8StringEncoding];
realMatchData contains the above json string.
But if convert the matchData back to string again to check what is being sent using,
NSString *str = [[NSString alloc] initWithData:matchData encoding:NSUTF8StringEncoding];
I get back the following json string
{
"moves" : "30",
"turn" : "0",
"player2" : "G:0000177110",
"p1score" : "0",
"player1" : "G:0000177110",
"movements" : "MVS",
"p2score" : "0"
}
keys pb1 and pb2 are missing.
I event tried to pass the values of pb1 and pb2 as nested json but problem remains the same, they keys are missing when sending data.
Is the right way to share the game state or should I use some other approach to share data ?
Thanks.
This does not answer the question exactly, but it may solve the problem, and the asker asked for an example. Apple provides its own JSON serialization which produces an NSData object from JSON serializable objects like NSNumber, NSArray, NSString, NSDictionary, etc.
NSMutableArray* matchArray = [NSMutableArray array];
/*
Fill the match array with the appropriate objects to represent your game state...
You've presumably already done this in order to get that string object...
*/
NSData* matchData = [NSJSONSerialization dataWithJSONObject: matchArray
options: 0 //pretty sure all the options here are irrelevant for our purposes
error: NULL]; //pass in a pointer to an NSError if you are interested in the error
//end the turn or do whatever one does in a non-turn-based match with matchData as the data object

how to get JSON object from server as it (in the same order) iPhone

Is there any way to get JSON object from the server in the same order??
For example when i fitch using browser my JSON object return like this:
{
"23": {
"numberOfRecords": "3",
"startDate": "27/11/2013",
"endDate": "31/12/2014",
"question": "How do you rate the new MenaME Portal ?",
"voteScale": "5",
"questions": {
"option1": {
"value": "1",
"option": "Poor",
"voteResult": "50.000"
},
"option2": {
"value": "2",
"option": "Acceptable",
"voteResult": "0.000"
},
"option3": {
"value": "3",
"option": "Good",
"voteResult": "0.000"
},
"option4": {
"value": "4",
"option": "Very Good",
"voteResult": "0.000"
},
"option5": {
"value": "5",
"option": "Excellent",
"voteResult": "50.000"
}
},
"selectedAnswer": "0",
"voteAnswered": "0",
"votes": "6"
}
}
after parsing it with [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error]
the object returned like this :
{
23 = {
endDate = "31/12/2014";
numberOfRecords = 3;
question = "How do you rate the new MenaME Portal ?";
questions = {
option1 = {
option = Poor;
value = 1;
voteResult = "50.000";
};
option2 = {
option = Acceptable;
value = 2;
voteResult = "0.000";
};
option3 = {
option = Good;
value = 3;
voteResult = "0.000";
};
option4 = {
option = "Very Good";
value = 4;
voteResult = "0.000";
};
option5 = {
option = Excellent;
value = 5;
voteResult = "50.000";
};
};
selectedAnswer = 0;
startDate = "27/11/2013";
voteAnswered = 0;
voteScale = 5;
votes = 6;
};
}
Is there any way or framework to get the object as it (in the same order returned from the server) ??
Dictionaries, both in JSON and NSDictionary, are unordered, meaning that it is irrelevant which order you see things in the log. This is defined in the JSON specification and the documentation for NSDictionary.
If it actually matters what order things are displayed in, then either the API you are linking to isn't using correct JSON, or you're doing something wrong in your app. To help with those situations you can use several of the sorted NSDictionary implementations that are around.
Can I ask why you want to ensure the dictionary is maintained in the correct order?
I understand in some cases (mine) an ancient JSON -> XML web service was being called by my app and the client refused to adjust the service so it could accept unordered JSON (valid json) but if you're writing the app, why do you need to ensure that it is in order?
I have a NSMutableDictionary subclass that keeps objects added by setObject:forKey in the order you call the method that can be found here.
It works by storing a NSMutableOrderedSet of keys within the dictionary and then overrides the keyEnumerator method to return an enumerator based on the ordered set
- (NSEnumerator *)keyEnumerator
{
return [self.orderedSetOfKeys objectEnumerator];
}
You could modify the NSMutableDictionary subclass i created to expose the NSMutableOrderedSet in the public header and then modify this set yourself to get an ordered version of your dictionary.. For example:
NSDictionary *JSONWebServiceDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
LNOrderedMutableDictionary *orderedDictionary = [[LNOrderedMutableDictionary alloc] initWithDictionary:JSONWebServiceDictionary];
NSMutableOrderedSet *order = [[NSMutableOrderedSet alloc] initWithArray:#[#"key1",#"key2",#"key3"]]; //All the keys you are expecting and the order you want them in..
orderedDictionary.orderSet = order; //orderSet does not exist.. it is currently called `array` and not exposed in LNOrderedMutableDictionary.h
I haven't tested the code above but unless you want to create or modify an existing JSON parser then it seems that it is your only option..
If you did want to modify an existing parser then it might just be as simple as replacing dictionary instances with LNOrderedMutableDictionary to keep everything in order.
Another idea to expand the above sample code could be to replace
NSMutableOrderedSet *order = [[NSMutableOrderedSet alloc] initWithArray:#[#"key1",#"key2",#"key3"]];
with an array returned in the JSONWebServiceDictionary dictionary as arrays keep their order when parsed from JSON so maybe you could do this?
NSMutableOrderedSet *order = [[NSMutableOrderedSet alloc] initWithArray:[JSONWebServiceDictionary objectForKey:#"keyOrderArray"]]];
Look at what you have. If you test the result you got back from JSONObjectWithData (which we'll assume was declared as id jsonObject)
if ([jsonObject isKindOfClass:[NSDictionary class]) { ...
or
NSLog(#"The object type is %#", [jsonObject class]);
you will find that it is indeed an NSDictionary (or perhaps an NSMutableDictionary). That dictionary, as you can see from the dump (or infer from the nearly identical JSON) contains a single entry with a key of "23".
So let's cast the jsonObject to an NSDictionary and reference it:
NSDictionary* jsonDict = (NSDictionary*) jsonObject;
NSDictionary* entry23Dict = [jsonDict objectForKey:#"23"];
Now, if you NSLog entry23Dict you will discover it contains all of the above, absent the { 23 = ... } outermost dictionary.
You can then access, say, "questions" with
NSDictionary* questDict = [entry23Dict objectForKey:#"questions"];
From there the individual "option1", "option2", ... "option5" dictionaries can be accessed in a similar fashion. You simply proceed one layer at a time -- don't get overwhelmed by the entire structure. (It's often helpful, when you're first learning, to NSLog each "layer" as you "peel" it out of the containing structure.)
And, of course, you have all the standard facilities that are available to NSDictionary objects (and NSArray objects, should your JSON contain any [..] arrays). For instance, you can iterate on the keys of the dictionary with
for (NSString* key in jsonDict) {
NSLog(#"This entry's number is %#", key); // For the above will print "23"
NSDictionary* numberedDict = jsonDict[key]; // Using the "new" form of dictionary access
NSString* endDate = numberedDict[#"endDate"]; // Ditto
NSLog(#"The end date is %#", endDate);
}
This is a fairly common problem. It's also probably the most annoying part about iOS. (java doesn't have this issue at all). If you want to get back objects, take a look at restkit.org Specifically this answer may help: https://stackoverflow.com/a/8284343/836450

Get multiple values from a web service

I have a webservice, in which the parameter "upload_images" have more than one value. How can i get that values. I am using SBJson. Here is my response
{
"node_title": "thk",
"category": "Boating",
"description": "Fg",
"link": "",
"nid": "446",
"post date": "Mon, 11/25/2013 - 07:04",
"upload_images": "http://prod.kyzook.com/?q=sites/prod.kyzook.com/files/styles/medium/public/2013-11-25%2007%3A03%3A25%20%2B0000.png&itok=WIBTqzbC, http://prod.kyzook.com/?q=sites/prod.kyzook.com/files/styles/medium/public/2013-11-25%2007%3A03%3A58%20%2B0000.png&itok=AhoLUnou"
}
If I understand your question correctly, upload_images contains a string of comma-separated URLs and you want to extract them.
You can easily achieve this using NSString's method componentsSeparatedByString, for instance
NSString *uploadImages = response[#"upload_images"];
NSArray *imageURLs = [uploadImages componentsSeparatedByString:#", "];
where I assumed response to be a NSDictionary object holding the parsed response.
you can get the the "upload_images" as the string and can convert in the array using componentsSeparatedByString: method
NSString *uploadImages = [response objectForKey:#"upload_images"];
NSArray *imageUrlArray = [uploadImages componentsSeparatedByString:#", "];

Creating JSON file using NSDictionary with an array

I am attempting to create json data to send to a server via an HTTP POST request. The server will only accept a specific format of JSON, otherwise it will return an error. I can successfully create and upload the JSON file to the server, however I am getting the following error because I have not formatted my JSON incorrectly:
JSON Error Message: {
code = "-2";
message = "Validation error: {\"message\":{\"to\":[\"Please enter an array\"]}}";
name = ValidationError;
status = error;
}
As you can see, the server needs a complex JSON format with an Array of Values and Keys but I'm not sure how to do that. Below is my current code to create the JSON data:
//Create Array With TO Values
NSDictionary *toField = #{#"email" : emailField.text};
//Create Dictionary Values
NSDictionary *messageContent = #{#"subject" : #"APPNAME Registration Complete", #"from_email" : #"email#domain.com", #"to" : toField};
NSDictionary *mandrillValues = #{#"key" : #"APPKEY",
#"redirect_url" : #"PRIVATE-URL",
#"template_name" : #"app-registration",
#"template_content" : [NSNull null],
#"message" : messageContent
};
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:mandrillValues options:NSJSONWritingPrettyPrinted error:nil];
According to the server, I need an array with keys and values, similar to a nsdictionary. When I use an NSArray, though, I can't add values / keys. Any ideas on how I should go about doing this? Below is an example of the JSON format that the server will accept, am I doing everything right to follow this format? If not, what do I need to change to match the format?
{
"key": "example key",
"template_name": "example template_name",
"template_content": [
{
"name": "example name",
"content": "example content"
}
],
"message": {
"text": "example text",
"subject": "example subject",
"from_email": "message.from_email#example.com",
"from_name": "example from_name",
"to": [
{
"email": "example email",
"name": "example name"
}
],
}}
Looks like the "to" field expects an array. Unless the variable toField is an NSArray that contains dictionaries with keys and values as described, you're going to get a JSON that's not exactly like the one you want.
I would suggest outputting the description of the outgoing JSON to see exactly where there are differences.
Update
I saw the addition to your question -
NSDictionary *toField = #{#"email" : emailField.text};
Does not create an array. Try:
NSArray *toField = #[#{#"email" : emailField.text}];

Resources