Parsing JSON into usable dictionaries - ios

I'm currently building an food tracker iOS app (using swift 2) and I would like to have a database with all the foods (and their info) stored in the app and accessible.
The idea is that when some add a 'ice cream' to their meal, his calories/sugar/fat 'counters' increase by the respective nutritional value of the ice cream. (so that this data can be processed later on)
I have found a database of food in what seems like JSON format (see bellow) but I have no idea how to process all this data with swift so that I could access the number of calories in a specific ingredient for example.
So far I tried this:
let url = NSURL(string: "myURL")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
} else {
let jsonResult = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
print(jsonResult)
}
})
task.resume()
}
It allows me to process the JSON format into a dictionary that I can access but what I need (I think) would be maybe a dictionary of arrays and I can't manage to make it work with the JSON format that I have bellow.
[
{
"Description": "Juice",
"Energy(kcal)Per 100 g": 29,
},
{
"Description": "Alcoholic beverage, daiquiri, canned",
"Energy(kcal)Per 100 g": 125,
}
...
]
I admit my question wasn't quite clear at first (I'm really new at this I apologize) but I actually tried to research it on Stackoverflow before posting, but I haven't find something that works for my case. Sorry again and many thank you for taking the time to still answer it :)

Have a look into NSJSONSerialization. That is what you get for free once installed xcode and the SDK. And it is not that bad actually.
This is Ray's approach to Swifty Json:
http://www.raywenderlich.com/82706/working-with-json-in-swift-tutorial
This is what you find when you use the search. You will have to "translate" it to swift though.
How do I parse JSON with Objective-C?
You may want to look at RestKit for some more convenient way of dealing with JSON sources.
Give it a try. And when you run into concrete problems, then get back to SO.

Just give it a try
var arrDicts: [Dictionary<String, AnyObject>] = []
arrDicts = try! NSJSONSerialization.JSONObjectWithData(dataFromService!, options: NSJSONReadingOptions.AllowFragments) as! [Dictionary<String, AnyObject>]
dataFromService is the data that you have received from web service.

Answer for reference pruposes. How to do this in Objective-C
1- First get the info
a) If you are getting the JSON from an API or any online site:
//Set url of the JSON
NString *urlReq = #"http://www.URLHERE.com/PATH/TO/THE/JSON"
//Get data from the JSON
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlReq]];
//Parse JSON
if(jsonData != nil){ //If the response is nil, the next line will crash
NSArray *resultArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
//Do stuff with the result...
}
b) If you are getting the information from the Core Data:
//Get context
NSManagedObjectContext *context = [self managedObjectContext];
//Preapre your fetch
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Products" inManagedObjectContext:context];
NSFetchRequest *requestCoreData = [[NSFetchRequest alloc] init];
[requestCoreData setEntity:entityDescription];
//add predicates if needed
//Execute fetch
NSArray *resultArray = [context executeFetchRequest:requestCoreData error:nil];
//do stuff with the result....
2- Then parse the retrieved info
a) If you want a specific index:
NSString *description = resultArray[0][#"description"];
b) if you don't know what is the index you want (most likely what happens in your JSON):
BOOL found = NO;
int index = 0;
while(index < [resultArray count] && !found){
if([resultArray[index][#"description"] isEqualToString:#"Juice"])
found = YES;
else
++index;
}
if(found){
//'index' => index where the info you are searching can be found
}
else{
//The info couldn't be found in the array
}

Related

Getting specific index such students names from json data

I am developing simple app in iOS that gets data web api my code returned json data correctly but when I want to specific for example student name from json returned null.
Here is my data :
{
Sudent(
{
{"id":"20",
"name":"Alan",
"email":"simpl#gmail.com",
"phone":"1234567890",
"location":"London"},
{
"id":"40",
"name":"John",
"email":"simpl#gmail.com",
"phone":"1234567890",
"location":"usa"},
"id":"50",
"name":"Nora",
"email":"simpl#gmail.com",
"phone":"1234567890",
"location":"kenya"},
})}
Here my code that gets data as json and stored NSMutableArray named Student:
-(void)proxydidFinishLoadingData:(id)data InMethod:(NSString*)method
{
if ([method isEqualToString:#"getstudentdata"]){
defaultDict = [[NSMutableDictionary alloc]init];
[defaultDict addEntriesFromDictionary:[NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]];
Student = [defaultDict objectForKey:#"data"];
}
}
this codes it works ok and correctly and returned json data but the problem is when I want to get specific index such as names of students it is not work it returns null value.
the code that does this work is here:
NSMutableDictionary *response = [[defaultDict valueForKey:#"data"] mutableCopy];
NSString *name = [[response valueForKey:#"name"]];
];
NSLog(#"%#" ,name);
Please help me this to get student names
You are checking "name" key in "data" key, but in fact it is present in "Student" key.
Parse to Student object and using for loop get one by one "name" key value.
Use this site http://www.json4swift.com/ to create Model.(Just you have to Put JSON response here).
Var dataModelArray = [Your_Model_Type]()
if let data = data[“student”] as? [String: AnyObject] {
dataModelArray = Your_Model_type.modelsFromDictionaryArray(array: data)
}
//And you will have array of data in your dataModelArray
//Example you can access like this
dataModelArray[0].name

How can I access properties from an NSArray?

I'm getting data from a MySQL-database in JSON-format. In an Objective-C file the data gets modified and put into an NSMutableArray ("_data"). By the function "itemsDownloaded" the delegate gets notified as soon the download from the database is finished and receives the "_data"-array.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Create an array to store the data
NSMutableArray *_data = [[NSMutableArray alloc] init];
// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingAllowFragments error:&error];
// Loop through Json objects, create question objects and add them to our questions array
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
// Create a new data object and set its props to JsonElement properties
Data *newData = [[Data alloc] init];
newData.sozialversicherungsnummer = jsonElement[#"Sozialversicherungsnummer"];
newData.messzeitpunkt = jsonElement[#"Messzeitpunkt"];
newData.puls = jsonElement[#"Puls"];
newData.sauerstoffgehalt = jsonElement[#"Sauerstoffgehalt"];
// Add this question to the locations array
[_data addObject:newData];
}
// Ready to notify delegate that data is ready and pass back items
if (self.delegate)
{
[self.delegate itemsDownloaded:_data];
}
}
My aim is to access the properties "sozialversicherungsnummer", "messzeitpunkt", "puls" and "sauerstoffsättigung" of "newData" (in the above file). The class "Data" defines these four properties.
Now I want to display these properties inside of a chart in a swift file. For example I want to display "messzeitpunkt" on the x-axis and "puls" on the y-axis. I know how to handle the chart but my problem is that I don't know how to get access to the properties inside of the swift file.
If I write these lines into my swift file:
var data: NSArray = [];
func itemsDownloaded(items: [AnyObject]!) {
data = items
print("\(data)")
}
I get this on my output:
(
"<Data: 0x7ca5ff60>",
"<Data: 0x7ca5dab0>",
"<Data: 0x7be497e0>",
"<Data: 0x7ca42c00>"
)
Can somebody please help me?
The problem is that you don't want an NSArray. Swift doesn't know what's inside an NSArray. You want a Swift array, namely a [Data]. That way, Swift knows that each item is a Data, and you can access its properties.
Your output is:
(
"<Data: 0x7ca5ff60>",
"<Data: 0x7ca5dab0>",
"<Data: 0x7be497e0>",
"<Data: 0x7ca42c00>"
)
And that is exactly what you want and expect! You have an array of four Data objects. The only problem is that you have forgotten to tell Swift about this. You need to type the array as a [Data] or cast it to a [Data].
For example, where you are now saying:
func itemsDownloaded(items: [AnyObject]!) {
data = items
print("\(data)")
}
Try saying this:
func itemsDownloaded(items: [AnyObject]!) {
let datas = items as! [Data]
datas.forEach {print($0.messzeitpunkt)}
}
That is legal, because now you have told Swift what is in the array. And you will see that your data is there, exactly as you intend.

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.

Getting data from API, need to filter results

I'm working with data from a Foursquare API.
I want to get a list of coffee shops, and am getting that back correctly (I'm using RestKit)... but once I get that list, on my end I need to filter out any coffee shop that is a "Starbucks".
So right now I only know how to pull in all coffee shops, but I don't know how to parse that data once I have it before I serve it into the app table view so that there will be no Starbucks coffee shops listed.
Any ideas how I could do that? Let me know if you need any of my code snippets posted that might help. Thanks!
EDIT
Normal response type from the API would be:
"venue": [{
"name": "ABC Coffee Shop", {
So I would need to take "name" and filter out any name that was "Starbucks".
If FourSquare doesn't let you apply a filter to the request, to filter on the name "Starbucks" then what I would do with this is the following.
I would start by deserializing the response into a JSON Object, which in this case will be a dictionary.
NSError *error = nil;
NSDictionary *responseDict = [[NSJSONSerialization JSONObjectWithData:foursquareResponse options:0 error: &error];
NSArray *starbucks = nil;
if (!error) {
NSArray *coffeeShops = responseDict[#"venue"];
starbucks = [coffeeShops filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name = 'Starbucks'"]];
} else {
// handle the error
}
NSLog(#"Starbucks: %#", starbucks");
I didn't test this code but I think it should get you on your way.
Looks like JSON to me, you could just use the built in JSON parser, the NSJSONSerialization class. Here is a method I built that takes an NSData JSON parameter, deserializes it and returns a dictionary.
- (NSMutableDictionary *)deserialize: (NSData *)data {
return [[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error: nil] mutableCopy];
}
I don't know the structure of Foursquare's response inside out, so you might want to NSLog() the returned dictionary to see how you can now reference to it.

JSON Processing in iOS: Removing newlines

I am trying to decode json being sent to my application from Twitter's Streaming API.
Twitter will send my client data which has the following structure:
The body of a streaming API response consists of a series of newline-delimited messages, where "newline" is considered to be \r\n (in hex, 0x0D 0x0A) and "message" is a JSON encoded data structure or a blank line.
They basically separate tweets with \r\n. Sometimes they send you one tweet, sometimes they send you many at once.
Decoding the 'many' case is problematic for me. I use NSJSONSerialization class built into iOS. The problem is that it reads the first tweet and then see's the \r\n and considers this as garbage and returns an empty array and accompanying error.
To get around this problem, I use the following code to basically remove the newlines.
// json is the received json data
NSString* responseString = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding];
NSMutableArray* responseMutableArray = [[responseString componentsSeparatedByString:#"\r\n"] mutableCopy];
[responseMutableArray removeLastObject];
NSArray* responseArray = [NSArray arrayWithArray:responseMutableArray];
[responseMutableArray removeAllObjects];
NSError* error;
for(NSString* tweetString in responseArray)
{
NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:[tweetString dataUsingEncoding:NSUTF8StringEncoding] options: NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
[responseMutableArray addObject:dict];
}
//responseMutableArray contains the parsed tweets
The approach is, convert the data into a NSString, NSString into an array (by spliting by \r\n), array back into data and then parse the JSON into a dictionary. Its a messy process and I wonder if there is something more efficient way that I can do this.
Thanks very much
Vb
Disclaimer: I actually got here by encountering this issue myself.
I think the simplest solution would be a revision of #Elmundo 's answer that removes the newlines completely. It worked for me.
Note: this is Swift version 3.something
guard var string = String(data: data, encoding: .utf8) else {
return
}
string = string.stringByReplacingOccurrencesOfString("\r\n", withString: "")
guard let data = string.data(using: .utf8) else {
return false
}
do {
let json = try NSJSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
}catch let e as NSError {
print(e.description)
}
Try this:
NSString *string = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding]:
string = [NSString stringWithFormat:#"[%#]", [string stringByReplacingOccurrencesOfString:#"\\r\\n" withString:#","]];
NSArray *array= [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
Touch this:
let stringData = String(data: data, encoding: NSUTF8StringEncoding)
if let strData = stringData {
let newString = strData.stringByReplacingOccurrencesOfString("\r\n", withString: "\\r\\n")
if let newString = newString {
do {
let dict:[AnyObject] = try NSJSONSerialization.JSONObjectWithData(newString.dataUsingEncoding(NSUTF8StringEncoding)!, options: [.AllowFragments, .MutableContainers]) as! [AnyObject]
}catch let e as NSError {
print(e.description)
}
}
}

Resources