From my backend API, I get a json of objects consisting of array, dictionary, number, bool, string etc. For eg.
{
data:[
{
id : 1,
name : "abcd"
},
{
id : 2,
name : "abcde"
},
{
id : 3,
name : "abcde"
},
]
total_count : 10
}
Sometimes the value in total_count comes as a number and sometimes it comes as a string. In my code I have coded
[lbl setText:[jsonObject valueForKey:#"total_count"]]
This crashes because when the total_count key value is a number. Obviously I can do this
[lbl setText:[NSString stringWithFormat:#"%d",[[jsonObject valueForKey:#"total_count"] intValue]]];
but this happens at a lot of places in the API. A string is coming instead of a bool.
data:false instead of data:[]
[EDIT]
[[AFHTTPRequestOperationManager manager] GET:[URLString attachToken] parameters:parameters success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
if([[[responseObject valueForKey:#"response"] valueForKey:#"status"] boolValue]) {
NSLog(#"success");
}
if(success)success(operation, **responseObject**);
} failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
if(failure)failure(operation, error);
if(operation.response.statusCode == 0) {
ATAFNetworkingRequestObject *obj = [[ATAFNetworkingRequestObject alloc] init];
obj.urlString = URLString;
obj.paramters = parameters;
obj.successBlock = success;
obj.failureBlock = failure;
obj.type = ATNetworkingRequestGET;
if(![self duplicateRequestExists:obj])[pendingAPICalls addObject:obj];
}
[self logAPIFailedWithOperation:operation parameters:parameters error:error];
} autoRetry:5 retryInterval:7];
do like after serilization based on your A string is coming instead of a bool. data:false instead of data:[]
if([datajsonObject isKindOfClass:[NSArray class]]){
//Is array
}else if([datajsonObject isKindOfClass:[NSDictionary class]]){
//is dictionary
}else if([datajsonObject isKindOfClass:[NSString class]])
{
//is String
}
else{
//is something else
}
You can check server value is Number or string as this
NSString *newString = [NSString stringWithFormat:#"%#",[[jsonObject valueForKey:#"total_count"]
if ([newString isKindOfClass:[NSNumber class]])
{
NSLog(#"It is number");
}
if ([newString isKindOfClass:[NSString class]])
{
NSLog(#"It is string");
}
Swift code :
lblCount.text = String(datajsonObject["total_count"] as AnyObject)
Objective c :
NSString *strCount = [NSString stringWithFormat:#"%#",[jsonObject valueForKey:#"total_count"]]
if ([strCount isKindOfClass:[NSString class]])
{
// Write your code to show on label
}
Related
Value Stored to 'sort' is never read.
Below is the Sample Code:
NSString *sortLabel = #"View";
if([MManager sharedInstance].sortBy.count > 0)
{
for (NSDictionary *sort in [MManager sharedInstance].sortBy) {
if ([[sort objectForKey:#"selected"] intValue] == 1) {
sortLabel = [NSString stringWithFormat:#"Sort by: %# ", [sort objectForKey:#"label"]];
}
}
}
else
{
for (NSDictionary *sort in [MActions sharedInstance].sorts) {
if ([[sort objectForKey:#"selected"] intValue] == 1) {
sortLabel = [NSString stringWithFormat:#"%# ", [sort objectForKey:#"label"]];
}
}
}
[self form:#"()" withText:[NSString stringWithString:sortLabel] andButton:self.sBtn];
You are assigning #"dummyValue" to sort but immediately changing it to something else.
Why not declare sort without assigning a value?
NSString *sort;
if(somecondition)
{
sort = [NSString stringwithFormat:#"Sorting... %#",anotherXProperty];
}
else
{
sort = [NSString stringwithFormat:#"Sorting... %#",anotherYProperty];
}
[self someFunctionWithText:sort];
}
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;
I have 100 key and value in nsmutabledictornary and i want to check that any value have null or not. Do you have any short function or technique?
I don't want to multiple line code like check every key and value. Your answer would be appreciated.
This code will give you the set of keys which have (non)null values. You can't store actual nil values in a dictionary, so [NSNull null] is assumed. The predicate is trivially alterable to any other condition.
NSDictionary *d = #{ #"a" : #"1", #"b" : [NSNull null] };
NSSet *nullKeys = [d keysOfEntriesPassingTest:^BOOL(NSString *key, id obj, BOOL *stop) {
return [d[key] isKindOfClass:[NSNull class]];
}];
NSSet *nonnullKeys = [d keysOfEntriesPassingTest:^BOOL(NSString *key, id obj, BOOL *stop) {
return [d[key] isKindOfClass:[NSNull class]] == NO;
}];
From here, you can use the keys to generate a corresponding dictionary, if needed.
NSMutableDictionary *nonNullDict = [NSMutableDictionary dictionary];
[d enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if ([nonnullKeys contains:key]) {
nonNullDict[key] = obj;
}
}];
If you don't need a separate list of keys, and just need the filtered dictionary, skip the first step and modify the second part to read as follows:
NSMutableDictionary *nonNullDict = [NSMutableDictionary dictionary];
[d enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSNull null]] == NO) {
nonNullDict[key] = obj;
}
}];
Write category on NSDictionary it will provide you null free dictionary. Here is the category I have written for myself.
code for .h file (interface)
#import <Foundation/Foundation.h>
#interface NSDictionary (CheckNull)
{
}
- (NSDictionary *)nullFreeDictionary;
#end
Code for .m file. (implementation)
#import "NSDictionary+CheckNull.h"
#implementation NSDictionary (CheckNull)
- (NSDictionary *) nullFreeDictionary
{
NSMutableDictionary *tempDictionary = [self mutableCopy];
for (NSString *key in tempDictionary.allKeys) {
NSString *value = [tempDictionary valueForKey:key];
if ([value isKindOfClass:[NSString class]]) {
if (value == (id)[NSNull null] || value == nil || value.length == 0) {
[tempDictionary setValue:#"" forKey:key];
}
}
}
return tempDictionary;
}
Call null free method on your dictionary using above category.
NSDictionary *dict = [dict nullFreeDictionary];
//To remove NULL from Dictionary
-(NSMutableDictionary *)removeNullFromDictionary : (NSMutableDictionary *)dict
{
// if (![dict isKindOfClass:[NSMutableDictionary class]])
// {
// }
dict = [[NSMutableDictionary alloc] initWithDictionary:dict];
for (NSString * key in [dict allKeys])
{
if ([dict[key] isKindOfClass:[NSNull class]])
{
[dict setValue:#"" forKey:key];
}
else if ([dict[key] isKindOfClass:[NSMutableDictionary class]]||[dict[key] isKindOfClass:[NSDictionary class]])
{
dict[key] = [self removeNullFromDictionary:[NSMutableDictionary dictionaryWithDictionary:dict[key]]];
}
else if ([dict[key] isKindOfClass:[NSMutableArray class]]||[dict[key] isKindOfClass:[NSArray class]])
{
dict[key] = [self removeNullFromArray:[NSMutableArray arrayWithArray:dict[key]]];
}
}
return dict;
}
//To remove NULL from Array
-(NSMutableArray *)removeNullFromArray : (NSMutableArray *)arr
{
// if (![arr respondsToSelector:#selector(addObject:)])
// {
// arr = [[NSMutableArray alloc] initWithArray:arr];
// }
arr = [[NSMutableArray alloc] initWithArray:arr];
for (int cnt = 0; cnt<[arr count]; cnt++)
{
if ([arr[cnt] isKindOfClass:[NSNull class]])
{
arr[cnt] = #"";
}
else if ([arr[cnt] isKindOfClass:[NSMutableDictionary class]]||[arr[cnt] isKindOfClass:[NSDictionary class]])
{
arr[cnt] = [self removeNullFromDictionary:[NSMutableDictionary dictionaryWithDictionary:arr[cnt]]];
}
else if ([arr[cnt] isKindOfClass:[NSMutableArray class]]||[arr[cnt] isKindOfClass:[NSArray class]])
{
arr[cnt] = [self removeNullFromArray:[NSMutableArray arrayWithArray:arr[cnt]]];
}
}
return arr;
}
I have a problem to communicate with a server. The webserver expects all parameters in the JSON object to be a string. So every number and every boolean in every container needs to be a string.
For my example I have a NSDictionary full of key values (values are all kinds of types - numbers, arrays etc.). For example:
{
"AnExampleNumber":7e062fa,
"AnExampleBoolean":0,
"AnExampleArrayOfNumber":[17,4,8]
}
Has to become:
{
"AnExampleNumber":"7e062fa",
"AnExampleBoolean":"0",
"AnExampleArrayOfNumber":["17","4","8"]
}
I tried the standard NSJSONSerializer but it doesn't give me any option to do what I need. I then tried to transform everything in the dictionary manually to be a string but that seems to be overhead. Does anyone have hint for me? Maybe a serializer that does just that or a function to convert any objects in a container to strings?
This is one way you could do it. It's non-optimized and has no error handling. It only supports the kinds of objects that NSJSONSerializer supports.
#import <Foundation/Foundation.h>
#interface NSObject(SPWKStringify)
- (id)spwk_stringify;
#end
#implementation NSObject(SPWKStringify)
- (id)spwk_stringify
{
if ([self isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)self;
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
for (NSString *key in [dict allKeys]) {
newDict[key] = [dict[key] spwk_stringify];
}
return newDict;
} else if ([self isKindOfClass:[NSArray class]]) {
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (id value in ((NSArray *)self)) {
[newArray addObject:[value spwk_stringify]];
}
return newArray;
} else if (self == [NSNull null]) {
return #"null"; // representing null as a string doesn't make much sense
} else if ([self isKindOfClass:[NSString class]]) {
return self;
} else if ([self isKindOfClass:[NSNumber class]]) {
return [((NSNumber *)self) stringValue];
}
return nil;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDictionary *dict = #{
#"AnExampleNumber": #1234567,
#"AnExampleBoolean": #NO,
#"AnExampleNull": [NSNull null],
#"AnExampleArrayOfNumber": #[#17, #4, #8],
#"AnExampleDictionary": #{#"innerKey": #[#55, #{#"anotherDict": #[#"foo", #[#1, #2, #"three"]]}]}
};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[dict spwk_stringify] options:NSJSONWritingPrettyPrinted error:NULL];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"result: %#", jsonString);
}
}
The output will be:
result: {
"AnExampleNumber" : "1234567",
"AnExampleNull" : "null",
"AnExampleDictionary" : {
"innerKey" : [
"55",
{
"anotherDict" : [
"foo",
[
"1",
"2",
"three"
]
]
}
]
},
"AnExampleBoolean" : "0",
"AnExampleArrayOfNumber" : [
"17",
"4",
"8"
]
}
Note: Please keep in mind that turning [NSNull null] into a string doesn't make any sense and might actually be misleading and dangerous.
Enjoy.
(I assume you mean NSJSONSerializer, not NSSerializer.)
I doubt you'll find a pre-rolled solution to this. It's not a general problem. As you note, this is incorrect JSON, so JSON serializers shouldn't do it.
The best solution IMO is just write the code to transform your NSDictionary into another NSDictionary that is in the form you want. If you really want to make it a generic solution, I suspect that a custom NSDictionary walker with isKindOfClass: is your best bet. Something like this should work:
NSDictionary *myStringDictForDict(NSDictionary *dict); // forward decl if needed
NSArray *myStringArrayForArray(NSArray *array) {
NSMutableArray *result = [NSMutableArray new];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]]) {
[result addObject:myStringArrayForArray(obj)];
} else if ([obj isKindOfClass:[NSDictionary class]]) {
[result addObject:myStringDictForDict(obj)];
} else {
[result addObject:[obj description]];
}
}];
return result;
}
NSDictionary *myStringDictForDict(NSDictionary *dict) {
NSMutableDictionary *result = [NSMutableDictionary new];
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]]) {
result[key] = myStringArrayForArray(obj);
} else if ([obj isKindOfClass:[NSDictionary class]]) {
result[key] = myStringDictForDict(obj);
} else {
result[key] = [obj description];
}
}];
return result;
}
My parse cloud code function is setup to return data in the form of JSON, like so:
response.success
({
"results": [
{ "Number of top categories": top2.length },
{ "Top categories": top2 },
{ "Number of matches": userCategoriesMatchingTop2.length },
{ "User categories that match search": userCategoriesMatchingTop2 }
]
});
What I want to do is query this JSON array in my Objective-C code, and perform certain actions based on what's being returned, through the use of the if statement on the bottom. For example, where it says:
if ([result intValue] == 1){
[self performSegueWithIdentifier:#"ShowMatchCenterSegue" sender:self];
}
I want to replace result intValue with a statement that says the value of "Number of matches" from the JSON data is equal to 1.
- (IBAction)nextButton:(id)sender
{
if (self.itemSearch.text.length > 0) {
[PFCloud callFunctionInBackground:#"eBayCategorySearch"
withParameters:#{#"item": self.itemSearch.text}
block:^(NSString *result, NSError *error) {
NSLog(#"'%#'", result);
NSData *returnedJSONData = result;
NSError *jsonerror = nil;
NSDictionary *categoryData = [NSJSONSerialization
JSONObjectWithData:returnedJSONData
options:0
error:&jsonerror];
if(error) { NSLog(#"JSON was malformed."); }
// validation that it's a dictionary:
if([categoryData isKindOfClass:[NSDictionary class]])
{
NSDictionary *jsonresults = categoryData;
/* proceed with jsonresults */
}
else
{
NSLog(#"JSON dictionary wasn't returned.");
}
if (!error) {
// if 1 match found clear categoryResults and top2 array
if ([result intValue] == 1){
[self performSegueWithIdentifier:#"ShowMatchCenterSegue" sender:self];
}
// if 2 matches found
else if ([result intValue] == 2){
[self performSegueWithIdentifier:#"ShowUserCategoryChooserSegue" sender:self];
//default to selected categories criteria -> send to matchcenter -> clear categoryResults and top2 array
}
// if no matches found, and 1 top category is returned
else if ([result intValue] == 2) {
[self performSegueWithIdentifier:#"ShowCriteriaSegue" sender:self];
}
// if no matches are found, and 2 top categories are returned
else if ([result intValue] == 2) {
[self performSegueWithIdentifier:#"ShowSearchCategoryChooserSegue" sender:self];
}
}
}];
}
}
From what I understand, the response JSON data that you get, is an array of dictionaries within a dictionary.
To retrieve each of those values, you may use the following steps:
Step 1:
Separate the array of dictionaries from the result dictionary into an NSArray object.
NSArray *resultArray = [resultDictionary objectForKey:#"results"];
Step 2:
Now that you have the resultArray, you can extract the values that you want as follows:
Suppose you want the value of NSNumber object "Number of matches",
You know that its the 3rd object in the resultArray, so its index is 2.
NSDictionary *dictionary = [resultArray objectAtIndex:2];
NSNumber *numberOfMatches = [dictionary objectForKey:#"Number of matches"];
Now you can use the [numberOfMatches intValue] wherever you want.
Hope this helps! :)