JSON array Parsing issue in iOS - ios

I've got a JSON Request that I'm able to add to an NSDictionary but I'm unable to access part of the JSON string. Here is a sample
{
"Resp": {
"Success": "True",
"who": {
"userid": 234,
},
"students": [
{
"ID": 1,
"name": John
},
{
"ID": 2,
"name": Jane
}
],
}
}
I am storing the data in an NSMutableDictionary and then passing that to my function which should run through each student and process them accordingly.
Here is what I've got so far and it's not working:
-(void)foo:(NSMutableDictionary*)json {
NSArray *students = json[#"students"];
for(NSMutableDictionary *student in students) {
NSLog(#"student id: %#", student[#"ID"]);
}
}
When debugging I can see the JSON object and students belongs under Resp as a value.

First of all, check your JSON on the validity http://jsonlint.com . It corrected your JSON with next modifications:
{
"Resp": {
"Success": "True",
"who": {
"userid": 234
},
"students": [
{
"ID": 1,
"name": "John"
},
{
"ID": 2,
"name": "Jane"
}
]
}
}
Then you should access your elements in right way:
- (void)foo:(NSMutableDictionary *)json {
NSArray *students = json[#"Resp"][#"students"];
for(NSDictionary *student in students) {
NSLog(#"student id: %#", student[#"ID"]);
}
}
For now it should be working.

Related

Invalid ReplaceNamedRangeContent request

I can successfully create a name range using batchUpdate api, however, replaceNameRangeContent gives the following error:
{ "error": { "code": 400, "message": "Invalid requests[0].replaceNamedRangeContent: Named range with ID kix.ydbik9q4xmna contains content that cannot be replaced.", "status": "INVALID_ARGUMENT" } }
The request for this was:
{ "requests": [ { "replaceNamedRangeContent": { "namedRangeId": "kix.ydbik9q4xmna", "text": "" } } ] }
Am I using the API properly? I created the name range giving start/endindex and naming it. It creates the NameRangeId that I used in the replace content post.
There was content in the index range above. However, below is a sample doc structure for an empty doc (with doc styles, etc. omitted). I tried a
{
"requests": [
{
"createNamedRange": {
"range": {
"startIndex": 1,
"endIndex": 2
},
"name": "ApplicantName"
}
}
]
}
followed by
{
"requests": [
{
"replaceNamedRangeContent": {
"namedRangeId": "kix.f7g9w0sr3kyp",
"text": "Applicant Name"
}
}
]
}
and get the same error. But when doing the create named range it would not accept any start/end index besides 1 and 2.
I must not understand the actual intent of named ranges!
Sample:
{
"title": "Test mule",
"body": {
"content": [
{
"endIndex": 1,
"sectionBreak": {
"sectionStyle": {
"columnSeparatorStyle": "NONE",
"contentDirection": "LEFT_TO_RIGHT",
"sectionType": "CONTINUOUS"
}
}
},
{
"startIndex": 1,
"endIndex": 2,
"paragraph": {
"elements": [
{
"startIndex": 1,
"endIndex": 2,
"textRun": {
"content": "\n",
"textStyle": {}
}
}
],
"paragraphStyle": {
"namedStyleType": "NORMAL_TEXT",
"direction": "LEFT_TO_RIGHT"
}
}
}
]
}
I can reproduce your error when creating a named range with "startIndex": 0
Mind that indices for paragraphs (and thus text) in Google Docs refer to the position of a character in the text and start with 1.
So if you create a name range specifying the indices correctly, you will not get the error mesage
{ "error": { "code": 400, "message": "Invalid requests[0].replaceNamedRangeContent: Named range with ID kix.ydbik9q4xmna contains content that cannot be replaced.", "status": "INVALID_ARGUMENT" } }
Sample request for creating named ranges:
{
"requests": [
{
"createNamedRange": {
"range": {
"startIndex": 5,
"endIndex": 7
},
"name": "test3"
}
}
]
}

how to parse complex json data in iOS?

I am new to iOS and I want to parse data but it is so complex, I don't know how to parse it. Below given is the json data.
{
"response_code": 200,
"last_updated": {
"date": "2015-12-27",
"time": "01:32:13"
},
"trains": [
{
"train": {
"number": "04921",
"start_time": "04:45",
"name": "SRE-UMB MEMU SPECIAL",
"type": "HSP"
},
"dest": {
"code": "UMB",
"name": "AMBALA CANT JN"
},
"source": {
"code": "SRE",
"name": "SAHARANPUR"
}
},
{
"train": {
"number": "04922",
"start_time": "20:45",
"name": "UMB-SRE MEMU SPECIAL",
"type": "HSP"
},
"dest": {
"code": "SRE",
"name": "SAHARANPUR"
},
"source": {
"code": "UMB",
"name": "AMBALA CANT JN"
}
}
]
}
{ , , } - it's a dictionary
[ , , ] - it's an array
In your case you get:
First level - dictionary with keys response_code, last_updated, trains.
Where:
response_code - value
last_updated - dictionary with keys date, time
trains - array of dictionaries with keys train, dest, source
etc.
Use the NSJSONSerialization class, it's easy.
For example, in Objective-C:
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (jsonObject) {
// jsonObject is an NSArray or NSDictionary representation of the data that you can now do something with
}

iOS JSON Parsing, array with multiple array

I have a JSON array with multiple object and I don't know how do I grab the "url" tag as an NSArray or a NSDictionary and show that image url in Tableview. I can't change the JSON data format.How should I do this?
Here is what the JSON response looks like:
{
"meta": {
"total_count": 10
},
"pages": [
{
"id": 7,
"meta": {
"type": "dashboard.NewsEvents",
"detail_url": "http://suno.to/api/v1/pages/7/"
},
"title": "NoEvent",
"created_at": "2016-03-06T10:42:19.646000Z",
"cover_url": [
[
{
"url": "/media/images/Maha_Shivratri2.original.jpg",
"title": "Maha Shivratri2.jpg"
},
{
"url": "/media/images/Maha_Shivratri1.original.jpg",
"title": "Maha Shivratri1.jpg"
}
],
[
{
"url": "/media/images/Celebrations.original.jpg",
"title": "Celebrations.jpg"
},
{
"url": "/media/images/Crew.original.jpg",
"title": "Crew.jpg"
},
{
"url": "/media/images/World_record.original.jpg",
"title": "World record.jpg"
},
{
"url": "/media/images/AI_pilots.original.jpg",
"title": "AI pilots.jpg"
}
],
[
{
"url": "/media/images/CbVv-VbWEAAmwv_.original.jpg",
"title": "DAL SWARAJ YATRA"
},
{
"url": "/media/images/CbVv_-TWwAE7RjM.original.jpg",
"title": "DAL SWARAJ YATRA"
},
{
"url": "/media/images/CbVv_SmXIAALQP8.original.jpg",
"title": "DAL SWARAJ YATRA"
},
{
"url": "/media/images/CahEc--UkAArc_z.original.jpg",
"title": "DAL SWARAJ YATRA"
}
]
]
},
{
"id": 2530,
"meta": {
"type": "dashboard.NewsEvents",
"detail_url": "http://suno.to/api/v1/pages/2530/"
},
"title": "World Culture Festival",
"created_at": "2016-03-12T06:59:21.023000Z",
"cover_url": [
[
{
"url": "/media/images/Security.original.jpg",
"title": "Security check"
}
],
[
{
"url": "/media/images/Elephant_statues.original.jpg",
"title": "Elephant"
}
],
[
{
"url": "/media/images/6.original.jpg",
"title": "Stage"
},
{
"url": "/media/images/4.original.jpg",
"title": "Stage"
}
]
]
},
{
"id": 2675,
"meta": {
"type": "dashboard.NewsEvents",
"detail_url": "http://suno.to/api/v1/pages/2675/"
},
"title": "Holi in Barsana",
"created_at": "2016-03-17T12:35:09.308000Z",
"cover_url": [
[
{
"url": "/media/images/Brajwasi_playing_holi_.original.jpg",
"title": "Holi in Barsana"
},
{
"url": "/media/images/dancing_.original.jpg",
"title": "Holi in Barsana"
},
{
"url": "/media/images/holi.._.original.jpg",
"title": "Holi in Barsana"
},
{
"url": "/media/images/holi..._.original.jpg",
"title": "Holi in Barsana"
}
],
[
{
"url": "/media/images/Lathmar_holi_19_n54f7LJ.original.jpg",
"title": "Lathmar Holi in Barsana"
}
],
[
{
"url": "/media/images/Lathmar_holi_17.original.jpg",
"title": "Lathmar Holi in Barsana"
},
{
"url": "/media/images/Lathmar_holi_20.original.jpg",
"title": "Lathmar Holi in Barsana"
}
]
]
},
I'm using this code to get the "url" array. Plz correct me ?
NSArray *imageUrlArray = [[self.jsonData objectAtIndex:indexPath.row]objectForKey:#"cover_url"];
NSLog(#"IMAGE URL ARRAY:%#",imageUrlArray);
NSString *imageUrl = [imageUrlArray valueForKey:#"url"];
NSLog(#"IMAGE URL:%#",imageUrl);
To view the JSON structure - http://jsonviewer.stack.hu/
NSMutableArray* imageurlArray = [NSMutableArray new];
NSArray* jsonArray = jsonData[#"pages"];
for (int i = 0; i<[jsonArray count]; i++) {
NSArray* coverUrlArray = jsonArray[i][#"cover_url"];
for (int t = 0; t< [coverUrlArray count]; t++) {
NSArray* UrlArray = coverUrlArray[t];
for (int x = 0; x<[UrlArray count]; x++) {
[imageurlArray addObject:UrlArray[x][#"url"]];
}
}
}
NSLog(#"imageurlArray: %#", imageurlArray);
//imageurlArray contains all url
//In cell for row at indexpath --> use imageurlArray[indexPath.row];
You have array wrappped by another array.
So, use this direction:
NSDictionary *json=//..initialized
NSArray *pages = [json valueForKey#"pages"];
NSDictionary *page = [pages objectAtIndex:0];
NSArray *ar1 = [page valueForKey#"cover_url"];
NSArray *ar2 = [ar1 objectAtIndex:0];
NSDictionary *elem = [ar2 objectAtIndex:0];
NSString *value = [elem valueForKey#"url"];
I would suggest using Mantel or JSONModel libraries for parsing objects and having decent DTOs.
When you deal with something like this, i suggest putting the complete json in http://jsonviewer.stack.hu/ so you can see the correct structure without getting confused.
Then it's just a matter of digging. When you see { }, you add a dictionary, when you see [ ], you add an array, until you reach your url object :)
I'm not writing the code because it is pretty trivial, just a mix of objectForKeys for dictionaries and objectAtIndex for arrays.
The last layers are just objects, so they're handled like any other object.
If you're confused about json, I suggest you try giving your json-parser a simple json (you hard core it yourself just above, its really just for testing).
Small advice :
Give it a simple array of 1 object, then 2, then put the array in a dict, then two, etc. and you keep making the json more complex until you really understand how it works. Then you'll eventually have a fake json just like your real one, and you can remove the fake and use the real one :)
Other advice :
There are many json parsing libraries that let you create the object model, where you can create (for example) a Page object that has an ID, a title, a cover URl, etc. that matches the JSON structure, and then you just tell the parser " make that JSON a Page!" and voilà, you have a Page. I don't know any of those json libraries in ios, but people will surely link it here, so try it out ! They're super easy to use and make json parsing really straightforward. And also, you don't have to map everything manually like you're doing ;)

Pulling Data from an NSDictionary

So I have an NSDictionary that has a variety of data within it. When printed to the log, it prints like this:
[{"user_id":3016817,"grade":"A","percent":"93","grading_periods":[{"assignments":[{"points":100.0,"grade":"A","score":95.0,"percent":"93","comment":null,"id":3268180},{"points":100.0,"grade":"A","score":90.0,"percent":"93","comment":null,"id":3268181}],"grade":"A","percent":"93","name":"Default"}]},{"user_id":3016818,"grade":"A","percent":"94","grading_periods":[{"assignments":[{"points":100.0,"grade":"A","score":92.0,"percent":"94","comment":null,"id":3268180},{"points":100.0,"grade":"A","score":95.0,"percent":"94","comment":null,"id":3268181}],"grade":"A","percent":"94","name":"Default"}]}]
If I use a formatter online, its a lot more readable and looks something like this:
[
{
"user_id": 3016817,
"grade": "A",
"percent": "93",
"grading_periods": [
{
"assignments": [
{
"points": 100,
"grade": "A",
"score": 95,
"percent": "93",
"comment": null,
"id": 3268180
},
{
"points": 100,
"grade": "A",
"score": 90,
"percent": "93",
"comment": null,
"id": 3268181
}
],
"grade": "A",
"percent": "93",
"name": "Default"
}
]
},
{
"user_id": 3016818,
"grade": "A",
"percent": "94",
"grading_periods": [
{
"assignments": [
{
"points": 100,
"grade": "A",
"score": 92,
"percent": "94",
"comment": null,
"id": 3268180
},
{
"points": 100,
"grade": "A",
"score": 95,
"percent": "94",
"comment": null,
"id": 3268181
}
],
"grade": "A",
"percent": "94",
"name": "Default"
}
]
}
]
My question would be how would I access the value of grade or score for a specific user_id using this dictionary?
Your string represents a NSArray, not a NSDictionary. And it's a JSON string, so you can parse it using NSJSONSerialization:
NSString *jsonString = #"..." // your string here
// Create array from json string
NSArray *jsonArray = [NSJSONSerialization
JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingMutableContainers
error:Nil];
// Loop to find your user_id
// Because each child of this array is a dictionary
for (NSDictionary *dic in jsonArray) {
if ([dic[#"user_id"] isEqual:#3016817]) { // user_id field is number
// Access what you want
NSString *grade = dic[#"grade"];
// For "score" you must go deeper
// Just remember, [] is array and {} is dictionary
}
}
A simple way to do this would be
for (NSDictionary* d in theArray) {
if ([d[#"user_id"] isEqualToString: u]) {
// do something
}
}
And matt is right, it is an array of dictionaries, no matter what type you declared to be.
You can use below class method of NSJsonSerialization class to create NSArray which will contain all the dictionaries.
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
Once you get the array of dictionaries from JSON, you can do following:
NSArray *filteredjsonArr = [jsonArr filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"user_id == %#", #"3016818"]];
NSDictionary *dict = [filtered firstObject];
if (dict != nil) {
NSString *grade = [dict objectForKey:#"grade"];
NSArray *gradingPeriods = [dict objectForKey:#"grading_periods"];
}
To access score and grade for specific assignments, you'll need to drill down further into gradingPeriods array.

Overcoat - Mapping multiple types of server responses

I cannot figure out how to map the response array when the response JSON has different envelope keys:
EXAMPLE Responses:
"response": {
"currencies": [
{
"id": 5,
"name": "British Pound",
"shortName": "GBP",
"symbol": "£",
"symbolPreceedsValue": true
},
{
"id": 6,
"name": "U.S. Dollar",
"shortName": "USD",
"symbol": "$",
"symbolPreceedsValue": true
},
{
"response": {
"countries": [
{
"id": 9,
"name": "United Kingdom",
"shortName": "GB",
"isMajorMarket": true
},
{
"id": 10,
"name": "USA",
"shortName": "US",
"isMajorMarket": true
},
I have set up a ServerResponse object:
#implementation ServerResponse
+(NSString *)resultKeyPathForJSONDictionary:(NSDictionary *)JSONDictionary
{
return #"response";
}
#end
// And setup what I believe to map the response for the Country
+ (NSDictionary *)responseClassesByResourcePath {
return #{
#"countries": [SPCCountry class]
};
}
It results in ONE SPCCountry object being created with null data. I want a LIST of SPCCountry / SCCurrency objects.
ANSWER:
For each unique response path, you need to create an OCVResponse subclass and return the class and path in the class method:
+ (NSDictionary *)responseClassesByResourcePath {
return #{
#"/install": [ServerResponse class],
#"/countries": [SPCCountryResponse class],
#"/currencies": [SPCCurrencyResponse class]
};
}
The implementation of the response classes need only implement:
#implementation SPCCountryResponse
+(NSString *)resultKeyPathForJSONDictionary:(NSDictionary *)JSONDictionary
{
return #"response.countries";
}
#end

Resources