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"];
Related
The title may be confusing, apologies. Maybe someone can advise me on a more appropriate title.
I have a .json file structured as below.
"sections": [{
"title": "Locations",
"key": "location"
}],
"contacts": [{
"title": "Social Worker",
"name": "Mrs X",
"office": "xxxxxxxx",
"location": "Lisburn",
"department": "xxxxxxxx",
"telephone": "xxx xxxxxxxx"
},...
When parsing this, I create an array called contactsArray. I can then create AEContact objects from this array like so:
for (NSDictionary *contactDic in [contactsArray valueForKey:#"contacts"]) {
// Create AEContact objects
_contact = [[AEContact alloc] initWithDictionary:contactDic];
[contacts addObject:_contact];
}
self.contacts = [contacts copy];
In the self.contacts array, the value for the contact.location property is what I am interested in. I need to create separate arrays of related AEContact objects based on the location property, then map these to the location key in my contactArray dictionary
This is what I have tried so far:
NSMutableDictionary *locationsDic = [NSMutableDictionary new];
// Loop through contacts
for (int i = 0; i < self.contacts.count; i++) {
// Sort by location
if ([self.contacts[i] valueForKey:#"location"] == [[[contactsArray valueForKey:#"contacts"] objectAtIndex:i] valueForKey:#"location"]) {
[locationsDic setValue:self.contacts[i] forKey:[[[contactsArray valueForKey:#"contacts"] objectAtIndex:i] valueForKey:#"location"]];
}
}
And the output is:
{
Ballynahinch = "<AEContact: 0x15dda1fc0>";
Bangor = "<AEContact: 0x15dda2210>";
Lisburn = "<AEContact: 0x15dda1c70>";
...
}
When an AEContact object has the same location, it sets it as another key/value in the dictionary and overwrites the previous entry. What I need to happen is something like this:
{
Lisburn = "<AEContact: 0x15dda18f0>",
"<AEContact: 0x15dda18f0>",
"<AEContact: 0x15dda18f0>";
Bangor = "<AEContact: 0x15dda18f0>",
"<AEContact: 0x15dda18f0>",
"<AEContact: 0x15dda18f0>";
}
I'm not sure if the output should should/will look like the preview above, I can only assume as I have not yet achieved my goal. How can I create the related AEContact objects in an array and map them to location key in my locationsDic? Thanks.
The title (and the problem description) are a little tough to follow, but I think you're trying to index an array of (AEContact) objects by their location parameter.
We can clarify this just with some tighter naming and with a find-or-create pattern as we process the input.
NSDictionary *jsonResult = // the dictionary you begin with
NSArray *contactParams = jsonResult[#"contacts"];
NSMutableDictionary *contactsByLocation = [#{} mutableCopy];
for (NSDictionary *contactParam in contactParams) {
NSString *location = contactParam[#"location"];
// here's the important part: find the array of aecontacts in our indexed result, or
// create it if we don't find it
NSMutableArray *aeContacts = contactsByLocation[location];
if (!aeContacts) {
aeContacts = [#[] mutableCopy];
contactsByLocation[location] = aeContacts;
}
AEContact *aeContact = [[AEContact alloc] initWithDictionary:contactParam];
[aeContacts addObject:aeContact];
}
contactsByLocation will be what I think you're looking for.
Order of json format is getting changed. I need the below format
{
"user_id": "",
"name": "",
"StDate": "07/16/2015 13:00",
"EdDate": "07/16/2015 13:00",
"detailed": [
{
"Stname": ""
},
]
}
What i am getting atlast is
{
"user_id" : "1",
"Detailed" : [
{
“Stname" : ""
},
"EdDate" : "08\/19\/2015 12:25:47",
"StDate" : "08\/19\/2015 12:25:47",
“name” : "",
}
After getting all values i am converting to json. I am using the following code.
NSError *error1;
NSString *jsonString1;
NSData *jsonData1 = [NSJSONSerialization dataWithJSONObject:dictjson1
options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
error:&error];
if (! jsonData1) {
NSLog(#"Got an error: %#", error1);
} else {
jsonString1 = [[NSString alloc] initWithData:jsonData1 encoding:NSUTF8StringEncoding];
NSLog(#"converted json string is %#",jsonString1);
}
Please advice.
JSON has two structures: objects and arrays. Arrays are indexed by integers, and ordered. Objects are indexed by strings, and unordered. You can't enforce order on JSON objects; it is implementation-dependent. If you need to access object attributes in a certain order, enumerate the keys in this order in an array.
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 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.
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