I have this JSON code:
[
{
"descrizione": "Risotto Giallo con Stinco",
"prezzo": null
},
{
"descrizione": "Orecchietta al ragu bolognese",
"prezzo": null
},
{
"descrizione": "Penne ai gamberi",
"prezzo": null
}
]
To print in a Text View, so that I wrote this code:
self.MenuGiorno.text = [self.MenuGiorno.text stringByAppendingString:[NSString stringWithFormat:#"%#",jsonArray1]];
Of course in this way I see in the Text View the complete code without parsing it.
How can I specify to print only "descrizione" and "prezzo"? Don't mind about Italian, this last two are the identifier I must use to fill the Text View. Someone has an idea?
The jasonArray1 is an array of dictionaries. First you must access the array element, there is only one, the dictionary. Then access the dictionary item by key name.
NSString *str = jsonArray1[0][#"descrizione"];
or more verbosely:
NSArray *fistArrayElement = [jsonArray1 firstObject];
NSString *str = [fistArrayElement objectAtIndex:#"descrizione"];
Related
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;
}
So the last two days I have been struggling to get data from and to a JSON file, this is because it has multiple levels and the same names. I did not set up this file and can't change the structure so I have to get it working in the way it is. To Pharse JSON form a single level is no problem and it works fine, what I need is to get separate data block from "GOV" and "PRIV" then I need a data block "GENERAL" and "LOCAL" and within those I need to be able to get the "Hospital information as a block but also the separate values. Now I have been trying to get this done for two days and I know im doing something wrong but cant figure it out. I do get data back for example the "GOV" block but then in the output window it is showing a array with access data (<__NSCFArray 0x7fe711f58800>) and the output... I cant break up this output and that is what I need because every value needs to be in a text file in a tableview cell. I know { } denotes NSDictionary [ ] denotes NSArray and I have been reading a lot about JSON and I get the concept but There is little to non for me understandable info when it comes to multi level JSON and equal names (hospital) in this case. I have tried all the available option I could find here on StackOverflow but no succes. So if somebody can push me in the right way I will be gratefull.. part of the code:
NSURL *url = [NSURL URLWithString:getDataURL];
NSData * data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_AppListArray = [[NSMutableArray alloc] init];
NSArray *wrapper= [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSDictionary *avatars = [wrapper objectAtIndex:0];
for(NSDictionary *apps in _jsonArray) {
if([[apps objectForKey:#"title"] isEqualToString:#"GOV"]){
NSDictionary*tmp = [apps objectForKey:#"hospital"];
_AppListArray = [tmp objectForKey:#"area"];
}
}
//returns error because _ApplistArray is an array and it can't read the data from the objectkey
for (int i = 0; i < _jsonArray.count; i++)
{
NSString *appName = [[_AppListArray objectAtIndex:i] objectForKey:#"hospitalname"];
NSString *appCondition = [[_AppListArray objectAtIndex:i] objectForKey:#"condition"];
NSString *app avgrating = [[_AppListArray objectAtIndex:i] objectForKey:#"avgrating"];
[_AppListArray addObject:[[Applist alloc]initWithAppName:appName andAppCondition:appCondition andAppURL:appURL]];
}
The _ApplistArray does return the 1ste Hospital data block but as an array and this is were I get stuck.. I need to get another level deeper.....Again the solution probably is easy but JSON is something I never worked with this is my first go. The JSON where I need to get the data from:
[
-{
-hospital: {
-area: [
-{
-hospital: [
-{
hospitalname: "ABC",
avgrating: "2,6",
condition: "UPDATE NEEDED",
},
-{
hospitalname: "DEF",
avgrating: "4,2",
condition: "FINE",
},
],
name: "GENERAL"
}
]
},
title: "GOV"
},
-{
-hospital: {
-area: [
-{
-hospital: [
-{
hospitalname: "GHI",
avgrating: "3",
condition: "INSTALL NEW",
},
-{
hospitalname: "JKL",
avgrating: "0",
condition: "NEW",
},
],
name: "LOCAL"
}
]
},
title: "PRIV"
}
]
Here you go.
NSArray *hospitals = [jsonArray objectForKey:#"mainKey"];// I assumed you getting with some key but change based on your requirement
for (NSDictionary *mainData in hospitals){ // Start of Main Hospital
NSDictionary *hospital = [mainData objectForKey:#"hospital"];
NSArray *areas = [hospital objectForKey:#"area"];
for(NSDictionary *area in areas){// Start of Area
NSArray *innerHospitals = [area objectForKey:#"hospital"];
for(NSDictionary *innerHospital in innerHospitals){
NSString *hospitalName = [innerHospital objectForKey:#"hospitalname"];
NSString *avgrating =[innerHospital objectForKey:#"avgrating"];
NSString *condition =[innerHospital objectForKey:#"condition"];
// Do What Ever you want here
}
NSString *name =[area objectForKey:#"name"];
}// End Of Area
NSString *title =[mainData objectForKey:#"title"];
} // End of Main Hospital
I haven't tested it. But i assume this will work. Have a try and let me know what happens.
The problem is that you need 2 for loops for each "Area".
Area is an Array (1st loop) and each hospital is another Array (2nd loop).
And inside each hospital element is the dictionary with the values you need.
ignoring loops this is how you get the first hospitalname(ABC) assuming _AppListArray has the contents of Area
NSString *appName = _AppListArray[0][#"hospital"][0][#"hospitalname"];
For each 0 you will replace it with the counters for the for loops.
I have a JSON array(dictionary?) of objects that are themselves an array. I need to find a value within one of these arrays so that I can compare it later. Part of my JSON data:
[
{
"Name": "Exhibitor",
"Url": "api/congress/exhibitor",
"ResourceType": "Data",
"LastMod": 1389106977
},
{
"Name": "Workshop",
"Url": "api/congress/workshop",
"ResourceType": "Data",
"LastMod": 1389106977
},
{
"Name": "Speaker",
"Url": "api/congress/Speaker",
"ResourceType": "Data",
"LastMod": 1389106977
},
]
My method receives a table name as a parameter and returns a time stamp. How would I receive the time stamp (1389106977) for the table "workshop" for example? This seems so simple but I cannot work it out for 'nested' arrays/dictionaries.
Thanks,
edit:
This is the my code with trojanfoe's added to it.
NSError* localError;
NSMutableArray *syncDataArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (syncDataArray)
{
NSNumber *lastMod = nil;
for (NSDictionary *dict in syncDataArray)
{
NSLog(#"current table is: %#", dict[#"Name"]);
if ([tableName isEqualToString:dict[#"Name"]])
{
lastMod = dict[#"LastMod"];
//break;
}
}
NSLog(#"LastMod = %#", lastMod);
}
else
{
NSLog(#"syncDataArray is empty");
}
This works perfectly and makes sense
The JSON data looks like an array of dictionaries, so you can iterate over the array and test for the "Name" entry:
NSArray *jsonData = ...; // You have converted JSON to Objective-C objects already
NSNumber *lastMod = nul;
for (NSDictionary *dict in jsonData) {
if ([#"Workshop" isEqualToString:dict[#"Name"]]) {
lastMod = dict[#"LastMod"];
break;
}
}
if (lastMod) {
// You found it
}
(Note I am not certain the type of object used to store the "LastMod" object, so you might need to do some debugging to find out).
EDIT If you make extensive use of this data you should immediately convert the JSON data into an array of (custom) model objects, which will make it easier to manipulate the data as your app becomes more complex.
You have an array for dictionaries so it would look something like :
NSNumber *timestamp = [[JSON objectAtIndex:index] objectForKey:#"LastMod"];
NSNumber *timestamp = response[1][#"LastMod"];
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
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:#", "];