I am getting a Json from server by making a network request in my app.I am getting <null> value for some keys in Json object.My app gets crashed if this type of response is received.Please tell me how can i validate>?
I have tried this but it does not work all time.
if(!(user_post.username==(id)[NSNull null]) )
{
user_post.username=[dict_user_info objectForKey:#"name"];
if(user_post.username!=nil)
{
ser_post.username=[dict_user_info objectForKey:#"name"];
}
else
{
user_post.username=#"Username";
}
}
Consider testing the value for null so your program won't crash. Like this:
if([dict_user_info objectForKey:#"name"] != [NSNull null])
{
ser_post.username=[dict_user_info objectForKey:#"name"];
}
Create a Category of NSDictionary and add following method in it, which replaces null value with empty string for each key in dictionary.
- (NSDictionary *)dictionaryByReplacingNullsWithStrings
{
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == nul || object == NULL) {
//pointer comparison is way faster than -isKindOfClass:
//since [NSNull null] is a singleton, they'll all point to the same
//location in memory.
[replaced setObject:blank
forKey:key];
}
}
return [replaced copy];
}
Usage :
[yourJSONDictionary dictionaryByReplacingNullsWithStrings];
Read more about Category in iOS Tutorial 1 and Tutorial 2
yourJsonObject = [myDic valueforkey#"key"];
if(yourJsonObject != [NSNull null])
{
//not null
}
** you can also check whether object exist or not
if(yourJsonObject)
{
//exist
}
I think you've confused your logic. I am trying to stay true to your code, but let me know if the following is not what you intended:
if (dict_user_info[#"name"] != nil && [dict_user_info[#"name"] isKindOfClass:[NSNull class]] == NO) {
user_post.username = dict_user_info[#"name"];
if (user_post.username != nil) {
ser_post.username = user_post.username;
} else {
user_post.username = #"Username";
}
}
These are a couple of methods I wrote for my projects, try them :
/*!
* #brief Makes sure the object is not NSNull or NSCFNumber, if YES, converts them to NSString
* #discussion Sometimes JSON responses can contain NSNull objects, which does not play well with Obj-C. So when you access a value from a JSON and expect it to be an NSString, pass it through this method just to make sure thats the case.
* #param str The object that is supposed to be a string
* #return The object cleaned of unacceptable values
*/
+ (NSString *)cleanedJsonString:(id)str
{
NSString *formattedstr;
formattedstr = (str == [NSNull null]) ? #"" : str;
if ([str isKindOfClass:[NSNumber class]]) {
NSNumber *num = (NSNumber*) str;
formattedstr = [NSString stringWithFormat:#"%#",num];
}
return formattedstr;
}
/*!
* #brief Makes Sure the object is not NSNull
* #param obj Sometimes JSON responses can contain NSNull objects, which does not play well with Obj-C. So when you access a value from a JSON ( NSArray, NSDictionary or NSString), pass it through this method just to make sure thats the case.
* #return The object cleaned of unacceptable values
*/
+ (id)cleanedObject:(id)obj
{
return (obj == [NSNull null]) ? nil : obj;
}
/*!
* #brief A JSON cleaning function for NSArray Objects.
* #discussion Sometimes JSON responses can contain NSNull objects, which does not play well with Obj-C. So when you access a value from a JSON and expect it to be an NSArray, pass it through this method just to make sure thats the case. This method first checks if the object itself is NSNull. If not, then it traverses the array objects and cleans them too.
* #param arr The Objects thats supposed to be an NSArray
* #return The NSNull Cleaned object
*/
+ (NSArray *)cleanedJsonArray:(id)arr
{
if (arr == [NSNull null]) {
return [[NSArray alloc] init];
}
else
{
NSMutableArray *arrM = [(NSArray*)arr mutableCopy];
int i=0;
for (id __strong orb in arrM)
{
if (orb == [NSNull null])
{
[arrM removeObjectAtIndex:i];;
}
i++;
}
return arrM;
}
}
Just pass a JSON string, array or object to the appropriate method and the method will clean it for you.
Do yourself a favour and write a method that handles this and put it into an extension. Like
- (NSString*)jsonStringForKey:(NSString*)key
{
id result = self [key];
if (result == nil || result == [NSNull null]) return nil;
if ([result isKindOfClass:[NSString class]]) return result;
NSLog (#"Key %#: Expected string, got %#", key, result);
return nil;
}
You might even add some code that accepts NSNumber* results and turns them into strings, if that is what your server returns (some poster here had the problem that his server returned dress sizes as numbers like 40 or strings like "40-42" which makes something like this useful).
And then your code becomes one readable line
user_post.username = [dict_user_info jsonStringForKey:#"name"] ?: #"username";
I actually use several slightly different methods depending on whether I expect null, expect no value, expect an empty string or not, which gives me warnings when my assumptions are wrong (but always returns something that doesn't break).
try this:
if(!(user_post.username == (NSString *)[NSNull null]) )
Related
When parsing API responses, sometimes I can not rely on strings being embedded in quotation marks. ID's are a good example of this, where some API's will send the numerical ID as a string while some will send it as a number.
What is a good practice when parsing such a value? If I simply parse it to an NSString like so:
NSString *myID = (NSString *)message["myID"];
I can end up with an NSString object that somehow contains (long)123.
And using stringValue would cause issues when the value is actually already sent as a string (since NSString does not have a stringValue function).
A way that works, but is somewhat ugly, is this:
id myID = (NSString *)message["myID"];
if ([myID respondsToSelector:#selector(stringValue)])
{
myID = [myID stringValue];
}
You could do something like:
id myID = message["myID"];
if ([myID isKindOfClass:[NSString class]]) { ... }
else { ... }
As long as this logic is encapsulated inside data parser and is opaque for your api users (i.e. they will always get a string) any approach is fine, e.g.:
- (NSString*)parseID:(NSDictionary*)message {
id rawID = message["myID"];
if ([rawID isKindOfClass:[NSString class]]){
return rawID;
} else if ([rawID isKindOfClass:[NSNumber class]]) {
return [(NSNumber*)rawID stringValue];
} else {
// We might still want to handle this case.
NSAssert(false, #"Unexpected id type");
return nil;
}
}
Alternative is to define stringValue in extension, so any possible objet will respond to selector:
#implementation NSString(JSONStringParsing)
- (NSString *)stringValue {
return [self copy];
}
#end
Why not just use description?
NSArray *objects = #[
#NSIntegerMin,
#NSIntegerMax,
#"123456789"
];
for (id object in objects) {
NSString *stringObject = [object description];
NSLog(#"%# -> %# | %#", [object className], [stringObject className], stringObject);
}
i have some api call which returns some values and store it into Array while receiving i may get repeated values from the server which i suppose not to append when its already in Array and insert new Values,
here my sample codes :
-(void)getIdValue:(NSString*)getIdVal {
getTagIdList = [[NSMutableArray alloc]init];
BOOL isTheObjectThere = [getTagIdList containsObject: getIdVal];
NSLog(isTheObjectThere ? #"Yes" : #"No");
if (isTheObjectThere == NO){
[getTagIdList addObject:getIdVal];
NSLog(#"getTagIdList -->%#",getTagIdList);
} if (isTheObjectThere == YES){
return;
}
}
I want all the new data should be Stored in getTagIdList but i am getting only one record is storing
You are initialising the array within the method which is wrong. You should not be initiating the array where you try to store data.(which will remove all the old data stored in previous calls)
NSInteger count=[ArrayData count];
for (int i=0; i<count; i++) {
NSString *StringValue= ArrayData[i];
BOOL contains = [getTagIdList containsObject: StringValue];
if (contains==YES) {
NSLog(#"fail");
}
else{
[getTagIdList addObject: StringValue];
}
}
Here "ArrayData" is a json responce. getTagIdList is saving the string values.Try it i think this one is work.
From jegadeesh answer i updated your code
getTagIdList = [[NSMutableArray alloc]init];
-(void)getIdValue:(NSString*)getIdVal {
BOOL isTheObjectThere = [getTagIdList containsObject: getIdVal];
NSLog(isTheObjectThere ? #"Yes" : #"No");
if (isTheObjectThere == NO){
[getTagIdList addObject:getIdVal];
NSLog(#"getTagIdList -->%#",getTagIdList);
} if (isTheObjectThere == YES){
return;
}
}
I'm using the following code to check if a NSNumber has nil value. So I'm converting the NSNumber to string and Im checking if its length is 0. If it is of zero, Im returning NSNull else Im returning the number itself.
- (id)NSNullToNilForKey:(NSNumber *)number
{
if ([[number stringValue] length] == 0){
return [NSNull null];
}
return number;
}
Im invoking it as follows,
NSString *bodyString = [NSString stringWithFormat:#"[ \"%#\",{\"session_token\": \"%#\",\"request\": [\"GetAssigneeWiseCompliancesChart\",{\"country_id\": %#,\"business_group_id\": %#,\"legal_entity_id\": %#,\"division_id\": %#,\"unit_id\": %#,\"user_id\": %#}]}]",clientSessionId,clientSessionId,[self NSNullToNilForKey:countryId],[self NSNullToNilForKey:businessGroupId],[self NSNullToNilForKey:legalEntityId],[self NSNullToNilForKey:divId],[self NSNullToNilForKey:unitId], [self NSNullToNilForKey:userId]];
But the problem is that, though the if loop is getting invoked. The value returned from the if loop of NSNullToNilForKey is <null> instead of null. How can I sort this out?
You're creating a string from a format, all of the parameters are added by taking their description, what you're seeing is the description of the NSNull instance.
Your method should specifically return a string and you should choose explicitly what string you want to return.
- (id)NSNullToNilForKey:(NSNumber *)number
{
if ([[number stringValue] length] == 0){
return #"NSNull";
}
return number;
}
try this
change the type anyobject id to NSNumber *
- (NSNumber *)NSNullToNilForKey:(NSNumber *)number
{
if ([[number stringValue] length] == 0){
return [NSNull null];
}
return number;
}
I have the following response that returns from a block:
response: {
error = "";
success = 1;
}
I attempt to evaluate "success" but it never evaluates as equal to 1 only as "else":
NSLog(#"response: %#", responseObject);
NSInteger success = (NSInteger)responseObject[#"success"];
NSString *errorMessage = (NSString *)responseObject[#"error"];
if (success == 0) {
NSLog(#"success is false");
NSLog(#"JSON: %#", errorMessage);
saveCallback(NO, [self createErrorFromDescription:errorMessage]);
}else if (success == 1){
NSLog(#"success is true");
saveCallback(YES, nil);
}else{
NSLog(#"success is else");
NSLog(#"JSON: %#", errorMessage);
saveCallback(NO, [self createErrorFromDescription:errorMessage]);
}
What am I doing wrong?
NSInteger is a primitive and id is an object (actually a pointer to an object), in this case likely an NSNumber.
Directly casting the pointer to an object, to NSInteger will not transform it into a integer value type, it will just reinterpret the pointers memory as an integer.
To transform the number object to a integer value you would call integerValue on it.
(It could also be that the number is missing from the response or it could be returned as the NSNull object, hence the error checking below)
NSNumber *successNumber = responseObject[#"success"];
NSInteger success;
if (!successNumber || successNumber == [NSNull null]) {
// the response doesn't contain anything for the "success" key :(
}
// if successNumber is nil, this will be 0.
// if successNumber is the NSNull object, this will crash (unrecognized selector)
// Just be aware of both of those.
success = [successNumber integerValue];
NSInteger success = [responseObject[#"success"] integerValue];
Given that it appears you're using raw json, you need to be very careful that you don't use a NULL value; this will result in an exception.
Since there are many types of Null in Objective C, it's best to use class introspection to make sure your objects are valid.
NSDictionary *responseArray = responseObject;
NSInteger success=0;
if([responseObject isKindOfClass:[NSDictionary class]])
{
// responseObject is safe to subscript
NSNumber *successNumber = responseObject[#"success"];
// Use introspection; messaging nil doesn't cause an exception and returns nil
if ([successNumber isKindOfClass:[NSNumber class]])
{
// NSInteger is a primitive
success = [successNumber integerValue];
}
}
// If success is anything but zero, assume it's true
if (success)
{
NSLog(#"success is true");
}
else
{
NSLog(#"success is false");
}
Presumably, your success key is either 1 or 0, so you could simplify this code a bit. In general, though, this is how you'll want to deal with objects that may be NULL rather than simply nil.
I'm having an issue where I use NSMutableDictionaries returns values from NSDictionary.
Here is the warning message:
incompatible pointer types returning 'NSDictionary *' from a function
with result type 'NSMutableDictionary *'
Here is the code:
- (NSMutableDictionary *)dictionaryWithContentsAtPath:(NSString *)path
{
if ([path rangeOfString:#"/SessionStore"].location == 0)
{
return [_inMemoryCache objectForKey:[path stringByReplacingCharactersInRange:NSMakeRange(0, 13) withString:#""]];
}
if ([path rangeOfString:#"/PermanentStore"].location == 0)
{
return [NSDictionary dictionaryWithContentsOfFile:[self.persistentStoragePath stringByAppendingPathComponent:[path substringFromIndex:15]]];
}
return nil;
}
Please help me how to resolve this warning.
You need to ensure you're returning the right type. Your method states it's returning an NSMutableDictionary, but then returns only an NSDictionary.
Try this instead:
- (NSMutableDictionary*)dictionaryWithContentsAtPath:(NSString*)path {
if ([path rangeOfString:#"/SessionStore"].location == 0) {
return [[_inMemoryCache objectForKey:[path stringByReplacingCharactersInRange:NSMakeRange(0, 13) withString:#""]] mutableCopy];
}
if ([path rangeOfString:#"/PermanentStore"].location == 0) {
return [NSMutableDictionary dictionaryWithContentsOfFile:[self.persistentStoragePath stringByAppendingPathComponent:[path substringFromIndex:15]]];
}
return nil;
}
Note: added a call to mutableCopy to turn your literal NSDictionary into a mutable version, and in the second case, used called the dictionaryWithContentsOfFile method on the NSMutableDictionary subclass instead of the NSDictionary parent class.