Parse JSON array in Objective-C - ios

I have managed to extract the following array (which I am dumping to console) from some json. How can I get and print out the value for one of the elements, i.e. task?
Objective-C:
NSArray *array = [dict objectForKey:#"row"];
NSLog(#"array is: %#",array);
Console output:
array is: {
0 = 1;
1 = "send email";
2 = "with attachment";
ltask = "with attachment";
task = "send email";
userid = 1;
}

array looks like it is actually an NSDictionary, so reference the key to get the value for it.
NSLog(#"Task: %#", array[#"task"]);

the variable array doesn't seem to be NSArray . Does this work for you?
id array = [dict objectForKey:#"row"];
if([array isKindOfClass:[NSDictionary class]]){
NSLog(#"Value of task %#",array[#"task"]);
}

From the log, it looks like the output is an NSDictionary object, so to get the value of task key just do this
NSDictionary *myDict = dict[#"row"];
NSString *task = myDict[#"task"];
NSLog(#"task = %#", task);
if you want to confirm just check the class type using isKindOfClass: method
if([dict[#"row"] isKindOfClass:[NSDictionary class]]) {
NSDictionary *myDict = dict[#"row"];
NSString *task = myDict[#"task"];
NSLog(#"task = %#", task);
} else if([dict[#"row"] isKindOfClass:[NSArray class]]) {
NSArray *myArray = dict[#"row"];
NSDictionary *myDict = myArray[0];
NSString *task = myDict[#"task"];
NSLog(#"task = %#", task);
}

try
if ([[dictionary allKeys] containsObject:#"row"]) {
NSObject *objRow = dictionary[#"row"];
if(objRow){
if([objRow isKindOfClass:[NSArray class]]){
NSArray *arr = (NSArray *)objRow;
....
}
if([objRow isKindOfClass:[NSDictionary class]]){
NSDictionary *dic = (NSDictionary *)objRow;
....
}
}
}

Related

Objective C: How to check object type without a lot of if statements

I am trying to set my text string to a URL. The following code works, but I feel like I can refactor it to make it look neater.
NSString *text = #“”;
id json = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
if ([json isKindOfClass:[NSDictionary class]]) {
id data = json[#“data”];
if ([data isKindOfClass:[NSDictionary class]]) {
id value = data[#"value"];
if ([value isKindOfClass:[NSArray class]]) {
id url = [value valueForKey:#"url"];
if ([url isKindOfClass:[NSString class]]) {
text = url;
}
}
}
}
So far it has the whole "mountain of doom" going on and I want to know how can I check if the object type is correct without using so many if statements. Any tips or suggestions are appreciated.
Edit: This is the lite version of my code, but the concept is the same.
In my opinion, there are 2 ways to make it look neater and ignore if-else-nesting-hell.
Using return.
NSString *text = #“”;
id json = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
if (![json isKindOfClass:[NSDictionary class]]) {
return;
}
id data = json[#“data”];
if (![data isKindOfClass:[NSDictionary class]]) {
return;
}
id value = data[#"value"];
if (![value isKindOfClass:[NSArray class]]) {
return;
}
id url = [value valueForKey:#"url"];
if (![url isKindOfClass:[NSString class]]) {
return;
}
text = url;
Create a generic method which checks kind of class and return a safe value
- (id)safeValueFromObject:(id)object forKey:(NSString *)key class:(Class)valueClass {
if (![object respondsToSelector:#selector(valueForKey:)]) {
return [[valueClass alloc] init];
}
id result = [object valueForKey:key];
return [result isKindOfClass:valueClass] ? result : [[valueClass alloc] init];
}
Use
NSString *text = #"";
id json = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
id data = [self safeValueFromObject:json forKey:#"data" class:NSDictionary.class];
id value = [self safeValueFromObject:data forKey:#"value" class:NSArray.class];
id url = [self safeValueFromObject:value forKey:#"url" class:NSString.class];
text = url;

Change a dictionary's all value to string

I want to change the Dictionary's all value to String, how to do with it?
Such as:
{ #"a":"a",
#"b":2,
#"c":{
#"c1":3,
#"c2":4
}
}
I want convert to :
{ #"a":"a",
#"b":"2",
#"c":{
#"c1":"3",
#"c2":"4"
}
}
How to do with it? I think all the day.
If I use below method to traverse the dictionary values:
NSArray *valueList = [dictionary allValues];
for (NSString * value in valueList) {
// change the value to String
}
If the value is a dictionary, how about it?
So, someone can help with that?
You could do this with a recursive method, it changes all NSNumber values to NSString and calls itself for nested dictionaries. Since a dictionary cannot be mutated while being enumerated a new dictionary is created and populated:
- (void)changeValuesOf:(NSDictionary *)dictionary result:(NSMutableDictionary *)result
{
for (NSString *key in dictionary) {
id value = dictionary[key];
if ([value isKindOfClass: [NSDictionary class]]) {
NSMutableDictionary * subDict = [NSMutableDictionary dictionary];
result[key] = subDict;
[self changeValuesOf:value result:subDict];
} else if ([value isKindOfClass: [NSNumber class]]) {
result[key] = [NSString stringWithFormat:#"%#", value];
} else {
result[key] = value;
}
}
}
NSDictionary *dictionary = #{#"a": #"a", # "b":#2, #"c": #{#"c1": #3, #"c2":#4 }};
NSMutableDictionary *result = [NSMutableDictionary dictionary];
[self changeValuesOf:dictionary result:result];
NSLog(#"%#", result);
You can create category for a dictionary and add method some like stringValueForKey:.
The realisation can be something like this:
- (NSString)stringValueForKey:(NSString*)key
{
id value = self[key];
if( [value respondsToSelector:#selector(stringValue)])
return [value performSelector:#selector(stringValue)]
return nil;
}

Can't extract needed information from NSDictionary

I parse a XML file, and after parsing get a NSDictionary object (XMLDictionary named). I'm parsing this:
<result><node><id>27</id><name>Name 1</name><type>0</type><price>0</price><img>/upload/iblock/1a1/1a138b2d4da6e42398beeb21acc8e84f.png</img></node><node><id>28</id><name>Name 2</name><type>0</type><price>0</price><img>/upload/iblock/b72/b724d1550f93987a73b04974b5f7390e.png</img></node></result>
After that i'm trying this (titleArr - NSArray):
_titleArr = [[[[_xmlDictionary objectForKey:#"result"] objectForKey:#"node"] objectForKey:#"name"] valueForKey:#"text"];
In Run-time I get this error in the above line: "Thread 1: signal SIGABRT". How can i fix this problem?
NSError* parseError = nil;
_xmlDictionary = [XMLReader dictionaryForXMLString:myXMLString error:&parseError];
if (parseError != nil) {
NSLog(#"Error on XML parse: %#", parseError);
}
NSLog(#"The full dictionary is %#", _xmlDictionary);
NSDictionary* result = [_xmlDictionary objectForKey:#"result"];
NSLog(#"'result' = %#", result);
NSDictionary* node = [result objectForKey:#"node"];
NSLog(#"'node' = %#", node);
NSString* name = [node objectForKey:#"name"];
NSLog(#"'name' = %#", name);
At the very least, if it fails, the error will be isolated to a single operation. And the NSLogs will show you the values after each step.
Try this:
NSArray *arrResult = [_xmlDictionary objectForKey:#"result"];
for(int i = 0; i < [arrResult count]; i++)
{
NSArray *arrNode = [arrResult objectAtIndex:i] valueForKey:#"node"];
NSString *sName = [arrNode valueForKey:#"Name"];
NSLog(#"Name : %#",sName);
}
Edited Answer
NSArray *arrResult = [_xmlDictionary valueForKey:#"result"];
for (int i = 0; i < [arrResult count]; i++) {
NSDictionary *dicNode = [[arrResult objectAtIndex:i] valueForKey:#"Node"];
NSString *sName = [dicNode valueForKey:#"Name"];
NSLog(#"Name : %#",sName);
}

Parsing a JSON string into three arrays Objective C

I am trying to use the data which I read from a text file in objective c. The data I read from the text file is:
{"aps":{"alert":"Test 1!","sound":"beep.wav","badge":5,"Type":"Banking"},"acme1":"bar","acme2":42}|{"aps":{"alert":"Test 2!","sound":"beep.wav","badge":5,"Type":"Banking"},"acme1":"bar","acme2":42}|{"aps":{"alert":"Test 3!","sound":"beep.wav","badge":5,"Type":"Banking"},"acme1":"bar","acme2":42}|{"aps":{"alert":"Test 4!","sound":"beep.wav","badge":5,"Type":"Banking"},"acme1":"bar","acme2":42}|{"aps":{"alert":"Test 5!","sound":"beep.wav","badge":5,"Type":"Banking"},"acme1":"bar","acme2":42}
Once read, I split the file into an array with a delimiter of "|". I then want to further separate it into 3 different arrays: banking, fraud and investment based on the key "Type". However I cannot seem to reach parse the JSON string once I split it into the array. My view did load method is below:
- (void)viewDidLoad {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/AccountNotifications.txt", documentsDirectory];
NSString *fileContents = [[NSString alloc] initWithContentsOfFile:fileName usedEncoding:nil error:nil];
NSArray *fileData = [fileContents componentsSeparatedByString:#"|"];
if (fileContents != NULL)
{
bankingNotifications = [[NSMutableArray alloc] init];
fraudNotifications = [[NSMutableArray alloc] init];
investmentNotifications = [[NSMutableArray alloc] init];
for (i = 0; i < [fileData count]; i++)
{
NSString *notification = fileData[i];
NSDictionary *json = [notification JSONValue];
NSArray *items = [json valueForKeyPath:#"aps"];
if ([[[items objectAtIndex:i] objectForKey:#"Type"] isEqual: #"Banking"])
{
[bankingNotifications addObject:fileData[i]];
NSLog(#"Added object to banking array");
}
if ([[[items objectAtIndex:i] objectForKey:#"Type"] isEqual: #"Fraud"])
{
[fraudNotifications addObject:fileData[i]];
NSLog(#"Added object to fraud array");
}
if ([[[items objectAtIndex:i] objectForKey:#"Type"] isEqual: #"Investment"])
{
[investmentNotifications addObject:fileData[i]];
NSLog(#"Added object to investment array");
}
} }
There is an error with these three lines:
NSString *notification = fileData[i];
NSDictionary *json = [notification JSONValue];
NSArray *items = [json valueForKeyPath:#"aps"];
Could you please help me parse the JSON strings into the three mutable arrays? The error I am getting is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM objectAtIndex:]: unrecognized selector sent to instance 0x1d59db30'
If you create the text file yourself I would suggest you create a valid json object (as your data looks like it is supposed to be json) to keep your data nice and clean. similar to this:
{"aps":[{"type":"Banking","badge":5},{"Type":"Fraud","badge":12}]}
Then you can do following (this code is not tested, it can be that you have to amend it a bit) but i hope you'll get an idea :)
NSError* error = nil;
NSDictionary* dict = nil;
//serialising the jsonobject to a dictionary
dict = [NSJSONSerialization JSONObjectWithData:fileContents
options:kNilOptions
error:&error];
bankingNotifications = [[NSMutableArray alloc] init];
fraudNotifications = [[NSMutableArray alloc] init];
investmentNotifications = [[NSMutableArray alloc] init];
if (dict) {
NSArray *dataArray = [dict objectForKey:#"aps"];
NSDictionary* siteData = nil;
NSEnumerator* resultsEnum = [dataArray objectEnumerator];
while (siteData = [resultsEnum nextObject])
{
//
if( [[siteData objectForKey:#"Type"] isEqualToString: #"Banking"]) {
[bankingNotifications addObject:notification];
NSLog(#"Added object to banking array");
} else if ([[siteData objectForKey:#"Type"] isEqualToString: #"Fraud"])
{
[fraudNotifications addObject:notification];
NSLog(#"Added object to fraud array");
}
else if ([[siteData objectForKey:#"Type"] isEqualToString: #"Investment"])
{
[investmentNotifications addObject:notification];
NSLog(#"Added object to investment array");
}
}
}
The value for Key "aps" is a dictionary.
NSDictionary *item = [json valueForKeyPath:#"aps"];
if ([[item objectForKey:#"Type"] isEqualToString: #"Banking"])
{
[bankingNotifications addObject:notification];
NSLog(#"Added object to banking array");
}
else if ([[item objectForKey:#"Type"] isEqualToString: #"Fraud"])
{
[fraudNotifications addObject:notification];
NSLog(#"Added object to fraud array");
}
else if ([[item objectForKey:#"Type"] isEqualToString: #"Investment"])
{
[investmentNotifications addObject:notification];
NSLog(#"Added object to investment array");
}

Error in connectionDidFinishLoading that I can resolve

I have a simple JSON array that is returned from a zip code passed to a third party service.
http://api.geonames.org/findNearbyPostalCodes?postalcode=94115&country=US&radius=5&username=frequentz
I get an unknown error when trying to deserialize the results and I'm not sure what is going wrong.
Here is my connectionDidFinishLoading method, which fires as anticiapated but always fails...and I get the error in the last else if. Ideas?
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading...");
self.zipCodes = [NSMutableArray array];
NSError *error = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:receivedData options:NSJSONReadingAllowFragments error:&error];
if (jsonObject != nil && error == nil) {
NSLog(#"Successfully deserialized...");
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
NSDictionary *deserializedDictionary = (NSDictionary *)jsonObject;
NSLog(#"Deserialized JSON Dictionary = %#", deserializedDictionary);
for (NSDictionary *item in jsonObject) {
NSString *city = [item objectForKey:#"adminName2"];
NSString *stateAbbreviation = [item objectForKey:#"adminCode1"];
NSString *postalCode = [item objectForKey:#"postalCode"];
NSString *distance = [item objectForKey:#"distance"];
NSString *country = [item objectForKey:#"country"];
NSString *stateName = [item objectForKey:#"stateName"];
ZipCodes *zipCode = [[ZipCodes alloc] initWithName:city stateAbbrev:stateAbbreviation postalCode:postalCode distanceFromGPSZip:distance country:country stateFullName:stateName];
[self.zipCodes addObject:zipCode];
}
}
else if ([jsonObject isKindOfClass:[NSArray class]]){
NSArray *deserializedArray = (NSArray *)jsonObject;
NSLog(#"Deserialized JSON Array = %#", deserializedArray);
}
else {
/* Some other object was returned. We don't know how to deal
with this situation as the deserializer returns only dictionaries or arrays */
}
}
else if (error != nil){
NSLog(#"An error happened while deserializing the JSON data.");
}
}
I think you're using the wrong service --it should be ...findNearbyPostalCodesJSON.... To use the JSON service as far as I can tell from their website. This is their example URL:
http://api.geonames.org/findNearbyPostalCodesJSON?postalcode=8775&country=CH&radius=10&username=demo

Resources