Filtering Parsed JSON in Objective-C - objective-c

I'm trying to take out the "lasttradeprice" in https://www.allcrypt.com/api.php?method=singlemarketdata&marketid=672 but I can't seem to figure out how to grab the "lasttradeprice" piece.
How would I 'filter' the "price" out? None of the other information is relevant.
Current Code:
NSURL * url=[NSURL URLWithString:#"https://www.allcrypt.com/api.php?method=singlemarketdata&marketid=672"]; // pass your URL Here.
NSData * data=[NSData dataWithContentsOfURL:url];
NSError * error;
NSMutableDictionary * json = [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingMutableContainers error: &error];
NSLog(#"%#",json);
NSMutableArray * referanceArray=[[NSMutableArray alloc]init];
NSMutableArray * periodArray=[[NSMutableArray alloc]init];
NSArray * responseArr = json[#"lasttradeprice"];
for(NSDictionary * dict in responseArr)
{
[referanceArray addObject:[dict valueForKey:#"lasttradeprice"]];
[periodArray addObject:[dict valueForKey:#"lasttradeprice"]];
}
NSLog(#"%#",referanceArray);
NSLog(#"%#",periodArray);
NOTE: Keep in mind I've never worked with JSON before so please keep your answers dumbed down a tad.

Key value coding provides an easy way to dig through that data. Use the key path for the values you want. For example, it looks like you could get the array of recent trades using the path "return.markets.OMC.recenttrades" like this (assuming your code to get the json dictionary):
NSArray *trades = [json valueForKeyPath:#"return.markets.OMC.recenttrades"];
That's a lot more concise than having to dig down one level at a time.
The value returned for a given key by an array is the array of values returned by the array's members for that key. In other words, you can do this:
NSArray *recentprices = [trades valueForKey:#"price"];
And since that's just the next step in the key path, you can combine the two operations above into one:
NSArray *recentprices = [json valueforKeyPath:#"return.markets.OMC.recenttrades.price"];
The only down side here is that there's no real error checking -- either the data matches your expectations and you get back your array of prices, or it doesn't match at some level and you get nil. That's fine in some cases, not so much in others.
Putting that together with the relevant part of your code, we get:
NSURL *url = [NSURL URLWithString:#"https://www.allcrypt.com/api.php?method=singlemarketdata&marketid=672"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error = nil;
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingMutableContainers error:&error];
NSArray *recentprices = [json valueforKeyPath:#"return.markets.OMC.recenttrades.price"];
Update: I just noticed that you want the "lasttradeprice", not the array of prices. Given that, the key path to use is simply #"return.markets.OMC.lasttradeprice", and the value you'll get back will be a string. So replace the last line above with:
NSString *lastTradePrice = [json valueforKeyPath:#"return.markets.OMC.lasttradeprice"];

The value you want is buried a few dictionaries deep. One general idea might be to dig recursively, something like this:
- (BOOL)isCollection:(id)object {
return [object isKindOfClass:[NSArray self]] || [object isKindOfClass:[NSDictionary self]];
}
- (void)valuesForDeepKey:(id)key in:(id)collection results:(NSMutableArray *)results {
if ([collection isKindOfClass:[NSDictionary self]]) {
NSDictionary *dictionary = (NSDictionary *)collection;
if (dictionary[key]) [results addObject:dictionary[key]];
for (id deeperKey in [dictionary allKeys]) {
if ([self isCollection:dictionary[deeperKey]]) {
[self valuesForDeepKey:key in:dictionary[deeperKey] results:results];
}
}
} else if ([collection isKindOfClass:[NSArray self]]) {
NSArray *array = (NSArray *)collection;
for (id object in array) {
if ([self isCollection:object]) {
[self valuesForDeepKey:key in:object results:results];
}
}
}
}
Then call it like this:
NSMutableArray *a = [NSMutableArray array];
[self valuesForDeepKey:#"lasttradeprice" in:json results:a];
NSLog(#"%#", a);

Related

NSDictionary writeToFile fails while objects are valid, permission is 0k

Why NSDictionary cannot be written?? I have checked the content of the dictionary: all the instances are of NSString and NSNumber. I checked permissions: a text file with the same name at the same path is written well. Of course, my dictionary is not empty.
NSString *file = ...
NSDictionary *dict = ...
// check dictionary keys
BOOL wrong = NO;
for (id num in [dict allKeys]) {
if (![num isKindOfClass:[NSNumber class]]) {
wrong = YES;
break;
}
}
if (wrong) {
NSLog(#"First");
}
// check dictionary values
wrong = NO;
for (id num in [dict allValues]) {
if (![num isKindOfClass:[NSString class]]) {
wrong = YES;
break;
}
}
if (wrong) {
NSLog(#"Second");
}
if (![dict writeToFile:file atomically:YES]) {
// 0k, let's try to create a text file
NSLog(#"Names writing error!");
[#"Something here... .. ." writeToFile:file atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
Output: "Names writing error!"
Text file is created successfully.
Writing out a dictionary creates a property list, and according to the documentation all keys in a property list must be strings.
... and although NSDictionary and CFDictionary objects allow their keys to
be objects of any type, if the keys are not string objects, the
collections are not property-list objects.
NSNumber objects as keys are not supported.
As #vadian points out, you cannot write plist with numeric keys. But you can use NSKeyedArchiver:
NSURL *documents = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:false error:nil];
NSURL *fileURL = [documents URLByAppendingPathComponent:#"test.plist"];
// this will not work
NSDictionary *dictionary = #{#1: #"foo", #2: #"bar"};
BOOL success = [dictionary writeToFile:fileURL.path atomically:true];
NSLog(#"plist %#", success ? #"success" : #"failure");
// this will
fileURL = [documents URLByAppendingPathComponent:#"test.bplist"];
success = [NSKeyedArchiver archiveRootObject:dictionary toFile:fileURL.path];
NSLog(#"archive %#", success ? #"success" : #"failure");
And you can read it back with NSKeyedUnarchiver:
// to read it back
NSDictionary *dictionary2 = [NSKeyedUnarchiver unarchiveObjectWithFile:fileURL.path];
NSLog(#"dictionary2 = %#", dictionary2);
Note, you can do this with any class that conforms (and properly implements) NSCoding. Fortunately, NSDictionary conforms already. You have to make sure that any objects inside the dictionary, also conform (both NSString and NSNumber do). If you had a custom object in your dictionary, you'd have to make it properly conform yourself.
This is all described in the Archives and Serializations Programming Guide.

Converting a JSON file to NSMutableDictionary in Objective C?

I have a json file that looks like this:
{
"data":
{
"level": [
{
//bunch of stuff
}
]
}
}
Now I want to convert that into a array of levels that I can access. If I take away the {"data: part, then I can use this:
NSData *allLevelsData = [[NSData alloc] initWithContentsOfFile:fileLoc];
NSError *error = nil;
NSMutableDictionary *allLevels = [NSJSONSerialization JSONObjectWithData:allLevelsData options:kNilOptions error:&error];
if(!error){
NSMutableArray *level = allLevels[#"level"];
for (NSMutableDictionary *aLevel in level){
//do stuff with the level...
But I have to have the {"data: as part of the file, and I can't figure out how to get a NSData object out of the existing NSData object. Any ideas?
Don't you need to pull the level NSArray out of the data NSDictionary first?
NSData *allLevelsData = [[NSData alloc] initWithContentsOfFile:fileLoc];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:allLevelsData options:kNilOptions error:&error];
if(!error){
NSArray *levels = dataDictionary[#"data"][#"level"];
for (NSDictionary *aLevel in levels){
//do stuff with the level...
You won't get mutable objects back by default and declaring the variables as mutable doesn't make them so. Take a mutableCopy of the result instead (assuming you really do need mutability).
Why are you trying to prune ahead of time? If you decode the original JSON, you'll be able to extract the level array from the data dict in the decoded dict.
It's not clear what else you're trying to accomplish or why you are going the path you ask about. Note, this doesn't necessarily mean your path is wrong, just that without a clearer indication of what goal you're really trying to accomplish or what you've actually tried (and errored/failed, along with how it failed), you're likely only to get vague/general answers like this.

JSON Parsing Error (blank response)

Here's my code, when I run it, I get: "2014-10-26 19:02:09.153 App[27372:1281902] Price: (
)".
I was wondering why, no errors are being passed through and it honestly confuses me.
(I'm getting a blank response for "omc_usd_price")
#try
{
NSURL *url = [NSURL URLWithString:#"https://omnicha.in/api?method=getinfo"];
NSData *data=[NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *JSONStuff= [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingMutableContainers error: &error];
NSLog(#"%#",JSONStuff);
NSMutableArray * OMCArray = [[NSMutableArray alloc]init];
NSArray * responseArr = JSONStuff[#"omc_usd_price"];
for(NSDictionary * dict in responseArr)
{
[OMCArray addObject:[dict valueForKey:#"omc_usd_price"]];
}
NSLog(#"Price: %# test", OMCArray); // Here you get the Referance data
}
#catch (NSException *exception) {
NSLog(#"%#", exception);
}
#finally {
}
EDIT:
Tried this, I don't think I did this right either!
NSMutableArray * OMCArray = [[NSMutableArray alloc]init];
NSMutableArray * OMCArray2 = [[NSMutableArray alloc]init];
NSArray * responseArr = JSONStuff[#"response"];
NSArray * responseArr2 = JSONStuff[#"omc_usd_price"];
for(NSDictionary * dict in responseArr)
{
[OMCArray addObject:[dict valueForKey:#"response"]];
for(NSDictionary * dict2 in responseArr2)
{
[OMCArray addObject:[dict2 valueForKey:#"omc_usd_price"]];
}
}
NSLog(#"Price: %# test", OMCArray2); // Here you get the Referance data
Here is the JSONStuff dictionary:
{
"error":false,
"response":{
"block_count":96136,
"difficulty":12.18364177,
"netmhps":234.652099,
"seconds_since_block":694,
"avg_block_time":196.533,
"total_mined_omc":6426691.6,
"omc_btc_price":7.0e-6,
"omc_usd_price":0.0025,
"market_cap":15833.5909
}
}
As you can see, there is no entry in the dictionary named "omc_usd_price". There is a entry by that name in the dictionary name "response", but you didn't ask for that.
If you want to get omc_usd_price, as mentioned on your code, then you need to parse 2 dictionaries.
You must first parse the dictionary with the key response, and this will give you a new dictionary.
Then in this new dictionary you must parse/look for the key omc_usd_price.
Also, you're not getting an array back, but a double or float or even a string.

How to parse multiple json in objective-c?

I am trying to parse JSON in objective-c but am having trouble. The example in the tutorial I am following only goes to the first level after the parent node. I am trying to get data that is a bit deeper. Any advice on how to do this?
The elements I am trying to get:
Title: data.children[i].data.title
Thumbnail: data.children[i].data.thumbnail
Json: http://www.reddit.com/r/HistoryPorn/.json
NSURL *blogURL = [NSURL URLWithString:#"http://www.reddit.com/r/HistoryPorn/.json"];
NSData *jsonData = [NSData dataWithContentsOfURL:blogURL];
NSError * error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
self.blogPosts = [NSMutableArray array];
NSArray * blogPostsArray = [dataDictionary objectForKey:#"data"];
for (NSDictionary *bpDictionary in blogPostsArray) {
BlogPost * blogPost = [BlogPost blogPostWithTitle:[bpDictionary objectForKey:#"title"]];
blogPost.thumbnail = [bpDictionary objectForKey:#"thumbnail"];
blogPost.url = [NSURL URLWithString:[bpDictionary objectForKey:#"url"]];
[self.blogPosts addObject:blogPost];
}
With the new syntax it should be easier to gets keys in a nested dictionaries. You can know the full keys/indexes path by just drawing a tree, remember that a dictionary starts with braces, and an array starts with brackets. For example let's retrieve the "thumbnail" and "url" value for the first entry in the children array:
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if(!json)
{
// Always handle eventual errors:
NSLog(#"%#",error);
return;
}
NSString* thumbnail= json[#"data"][#"children"][0][#"data"][#"thumbnail"];
NSString* url= json[#"data"][#"children"][0][#"data"][#"url"];

Accessing JSON Data in Objective C

I've downloaded my JSON Data, but I'm having trouble accessing a specific object. From my JSON data, I'm trying to pull the most recent value from variableName = "Elevation of reservoir water surface above datum, ft";
Here is my code:
- (void)viewWillAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSURL *url = [NSURL URLWithString:#"http://waterservices.usgs.gov/nwis/iv/?sites=02334400&period=P7D&format=json"];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if (jsonData != nil) {
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error: &error];
if (error == nil)
NSLog(#"%#", result);
}
}
Edited: It's too much data to print the output, but here is how I access the object in JS. I can't seem to write a working for statement that will do the same in Obj-C:
var d = JSON.parse(responseText);
for (var i = 0; i < d.value.timeSeries.length; i++) {
if (d.value.timeSeries[i].variable.variableName == 'Elevation of reservoir water surface above datum, ft') {
var result = d.value.timeSeries[i].values[0].value[d.value.timeSeries[i].values[0].value.length - 1];
console.log(result);
}
This is pretty ugly but it should give you something to start with:
NSArray *timeSeries = [JSON valueForKeyPath:#"value.timeSeries"];
[timeSeries enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *variableName = [obj valueForKeyPath:#"variable.variableName"];
if ([variableName isEqualToString:#"Elevation of reservoir water surface above datum, ft"]) {
NSArray *values = [obj valueForKey:#"values"];
NSDictionary *value = [values objectAtIndex:0];
values = [value objectForKey:#"value"];
value = [values lastObject];
NSLog(#"%#", [value objectForKey:#"value"]);
}
}];
Note
There is no validation/range checking of any kind I'll leave that as an exercise for you to do
One thing that you may consider is using a tool to generate model classes for you. That way you can use dot accessors to make your life a little bit easier. In the Mac App Store JSON Accelerator or Objectify are pretty good options. You then pipe the NSDictionary into those model classes and it's pretty easy.