Unrecognized Selector on Calling allKeys - ios

I'm calling the following code on a number of JSON dictionaries within another dictionary (so in this case, NSDictionary objects with another NSDictionary.
-(NSString *)getAllDictionaryValues:(NSDictionary *)dict
{
NSString *output=[NSString stringWithFormat:#""];
for(NSString *key in [dict allKeys])
{
output = [NSString stringWithFormat:#"%#\n%#: %#",output, key, [dict objectForKey:key]];
}
return output;
}
When I run this code, however, I get
2014-03-30 01:27:35.565 WebServiceTest[48606:60b] -[__NSCFString allKeys]: unrecognized selector sent to instance 0x9270230
What is wrong about my call to allKeys?

Like when you sending message to this function it may be declare your code that dict is an NSDicationay object (when you call this function) but it not an dictionary may be an array of String or etc that why you getting the exception.
-(NSString *)getAllDictionaryValues:(NSDictionary *)dict
So first check your dict like
if([dict isKindOfClass:[NSArray class]]){
//is an array
}else if([dict isKindOfClass:[NSDictionary class]]){
//is an dictionary
}else if([dict isKindOfClass:[NSString class]]){
//is an string
}else{
//or its something else
}

May be you are inputting a string rather than a dictionary object in the calling method

That's obvious. As log said, you tried to call allKeys method on __NSCFString instance. It means that you sent NSString instead of NSDictionary to getAllDictionaryValues: method.

It means the dict object is NSString type. You can check the object like this:
if([dict isKindOfClass:[NSDictionary class]]) {
// dict is NSDictionary type of object
} else if([dict isKindOfClass:[NSString class]]) {
// dict is string type of object.
}

Related

-[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x9d0d720

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[jsonArray removeAllObjects];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
responseData = nil;
NSMutableArray *sdf = [(NSDictionary*)[responseString JSONValue] objectForKey:#"DataTable"];
NSMutableArray * myArray = [[NSMutableArray alloc] init];
NSMutableDictionary * myDict = [[NSMutableDictionary alloc] init];
if (([(NSString*)sdf isEqual: [NSNull null]])) {
// Showing AlertView Here
}else {
for (int i=0; i<[sdf count]; i++) {
myDict=[sdf objectAtIndex:i];
[myArray addObject:[myDict objectForKey:#"RxnCustomerProfile"]];
}
jsonArray=[myArray mutableCopy];
NSMutableDictionary *dict=[jsonArray objectAtIndex:0];
if ([dict count]>1) {
// Showing AlertView Here
}
}
}
Hi Everyone, I have an issue regarding the -[__NSArrayM objectForKey:]: .
Tried to solve but did not get the better solution for it. Please help me to
find the solution. Thanks In Advance
Below is the issues
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x19731d40'
This is a debugging problem and nobody can really solve it for you as you are using non-local variables whose definition and values are unknown, don't mention that you are using SBJSON (I guess), etc. But let's see if we can give you some pointers. Your error:
[__NSArrayM objectForKey:]: unrecognized selector sent to instance
That tells you that you sent a dictionary method (objectForKey) to an array (__NSArrayM). So somewhere you have an array when you think you have a dictionary.
Now you declare and allocate a dictionary:
NSMutableDictionary * myDict = [[NSMutableDictionary alloc] init];
but then assign to it:
myDict=[sdf objectAtIndex:i];
So this discards the dictionary you allocated and instead assigns whatever is at index i in the array sdf. How do you know, as opposed to think, that the element of the array is a dictionary? You don't test to check...
So where did sdf come from? This line:
NSMutableArray *sdf = [(NSDictionary*)[responseString JSONValue] objectForKey:#"DataTable"];
So that calls JSONValue on some unknown string, assumes the result is a dictionary (could it be an array? or a failure?), looks up a key (did your error come from this line?), and assumes the result is an array.
So what you need to do is go and test all those assumptions, and somewhere you'll find an array where you think you have a dictionary.
Happy hunting!
YOU FETCH THE VALUE IN ARRAY FORMAT AND YOU INTEGRATE METHOD IN DICTIONARY.
You do not need to iterate keys and values of dict can directly pass values to array inside else part like:
myArray = [sdf objectForKey:#"RxnCustomerProfile"];
Key RxnCustomerProfile itself containing array not dictionary.
Change your if else part use below code:
if (([(NSString*)sdf isEqual: [NSNull null]])) {
// Showing AlertView Here
}else {
myArray = [sdf objectForKey:#"RxnCustomerProfile"];
}
NSMutableArray *sdf = [(NSDictionary*)[responseString JSONValue] objectForKey:#"DataTable"];
Check Sdf
if([sdf isKindOfClass:[NSDictionary class]])
{
NSLog(#"Dictionary");
}
else if([sdf isKindOfClass:[NSArray class]])
{
NSLog(#"NSArray");
}
else if([sdf isKindOfClass:[NSMutableArray class]])
{
NSLog(#"NSMutableArray");
}
First of all it seems like your json is not actually correctly formatted. Without knowing what responseData looks like it's difficult to say exactly what is wrong. But in your code there are a few areas where it can be improved.
First of all you don't need to use [responseString JSONValue]. You can short circuit it entirely with
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
NSArray *sdf = responseDictionary[#"DataTable"];
Now, the rest all depends on the data in responseData.
But you can make your code a little bit cleaner with (if I understand what you're trying to achieve correctly:
NSMutableArray *myArray = [NSMutableArray array];
if ([sdf isEqual:[NSNull null]]) {
// Showing AlertView here
} else {
for (NSDictionary *myDict in sdf) {
[myArray addObject:dict[#"RxnCustomerProfile"]];
}
}
// No idea what you're trying to achieve here, but here goes:
jsonArray = [myArray mutableCopy];
NSDictionary *dict = jsonArray.first;
if (dict.count > 1) {
// Showing AlertView here
}
Some things to note. You make very liberal use of NSMutableArray and NSMutableDictionary for no apparent reason. Only use mutable if you're actually changing the array or dictionary.

objectForKey throws an exception on count if it contains a single value

I have a dictionary JSON dictionary like
{
county = "XXXXX";
states = (
"YYYYY",
"ZZZZZ"
);
}
After reading the JSON dictionary the data is in an array. I then want to create a different array with the data and I am using the following code
for (NSString *key in array) {
NSMutableArray *myData =[[NSMutableArray aalloc] init];
myData = [array objectForKey:key];
int count = [myData count];
}
When key is 'states', it works fine. If key is 'country', then it throws an exception on count because objectForKey returns a NSString. How to resolve this? How to find how many objects the objectForKey will return?
Check out this answer from #HDdeveloper:
if([object isKindOfClass:[NSString class]] == YES)
{
NSLog(#"String rx from server");
}
else if ([object isKindOfClass:[NSDictionary class]] == YES)
{
NSLog(#"Dictionary rx from server");
}
else if ([object isKindOfClass:[NSArray class]] == YES)
{
NSLog(#"Array rx from server");
}
Basically, after you get the objectForKey, then you need to check what kind of object that is and use it accordingly.
Your JSON contains a dictionary.
The key "states" in that dict contains an array, and the other key contains a string. Neither key contains an NSData object.
Arrays have a count method, but strings do not. That's why you're crashing.

Resilience in JSON parsing with ObjectiveC

A JSON API feed used by our iOS ObjectiveC app is a bit flaky, so sometimes a field is null.
When parsing JSON we use
NSDictionary *json = [self JSONFromResponseObject:responseObject];
Then try to use the fields with e.g.
[widgetIDArray addObject:widget[#"name"][#"id"]];
Where sometimes the "name" field will be a null. Do we:
1) Ask the API provider to clean up their flaky API code
2) Check for null each and every time we try to use something from the json dict
if ( ![widget[#"name"] isKindOfClass:[NSNull class]] )
3) Use try - catch
#try {
[widgetIDArray addObject:widget[#"name"][#"id"]];
}
#catch (NSException *exception)
{
NSLog(#"Exception %#",exception);
}
ANSWER:
Thanks for the answers, below. Here is the extension to NSObject I added that allows me to get deeply nested JSON items that may or may not be present.
First call with something like
self.item_logo = [self valueFromJSONWithKeyArray:event withKeyArray:#[#"categories",#"bikes",#"wheels",#"model",#"badge_uri"]];
Here is the code in NSObject+extensions.m
- (id) valueFromJSONWithKeyArray:(id)json withKeyArray:(NSArray *)keyArray
{
for (NSString * keyString in keyArray)
{
if ([json[keyString] isKindOfClass:[NSObject class]])
{
json = json[keyString]; // go down a level
}
else
{
return nil; // we didn't find this key
}
}
return json; // We successfully found all the keys, return the object
}
null in a JSON response isn't "flaky", it is absolutely standard.
Even if it was "flaky", any message that you receive from the outside is an attack vector that could allow an attacker to hack into your program, so resilience is required. Crashing when your receive a null allows a DOS attack against your application.
#try / #catch is awful. Exceptions are thrown in response to programming errors. You don't catch them, you fix your code.
How do you fix your code? Simple. Write a few helper methods in an NSDictionary extension.
First you don't know that json is a dictionary. So you add an NSDictionary class method where you pass in anything and it returns what you passed if it is a dictionary and nil (with appropriate logging) if it is anything else.
Next you assume that there is a dictionary under the key "name". So you write an extension "jsonDictionaryForKey" which returns a dictionary if there is one, and nil (with appropriate logging) if it is anything else.
And so on. Make your JSON parsing bullet proof if you want to call yourself a professional developer. For extra bonus points you add a method which will take a dictionary and list all keys that are present that you didn't ask for - so you know if your API is sending things that you don't expect.
You can delete all NSNULL values in your JSON object. Here is a function I used in my library to git rid of all null values in a JSON object.
id BWJSONObjectByRemovingKeysWithNullValues(id json, NSJSONReadingOptions options) {
if ([json isKindOfClass:[NSArray class]]) {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)json count]];
for (id value in (NSArray *)json) {
[mutableArray addObject:BWJSONObjectByRemovingKeysWithNullValues(value, options)];
}
return (options & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([json isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:json];
for (id<NSCopying> key in [(NSDictionary *)json allKeys]) {
id value = [(NSDictionary *)json objectForKey:key];
if (isNullValue(value)) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
[mutableDictionary setObject:BWJSONObjectByRemovingKeysWithNullValues(value, options) forKey:key];
}
}
return (options & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return json;
}
After all null values have been cleared, perhaps the exceptions will gone too.
An approach that's often used is categories similar to these on NSDictionary and NSMutableDictionary, that ignore nil and NSNull.
#interface NSDictionary (nullnilsafe)
- (ObjectType)nullnilsafe_objectForKey:(KeyType)aKey;
#end
#implementation NSDictionary
- (ObjectType)nullnilsafe_objectForKey:(KeyType)aKey
{
id obj = [self objectForKey:aKey];
if (obj && ![obj isKindOfClass:[NSNull class]] ){
return obj;
}
return nil;
}
#end
#interface NSMutalbeDictionary (nullnilsafe)
- (void)nullnilsafe_setObject:(ObjectType)anObject forKey:(id<NSCopying>)aKey;
#end
#implementation NSMutalbeDictionary
- (void)nullnilsafe_setObject:(ObjectType)anObject forKey:(id<NSCopying>)aKey
{
if (anObject && aKey && ![anObject isKindOfClass:[NSNull class]]){
[self setObject:anObject forKey:aKey];
}
}
#end

Parsing JSON using objective - c

Im getting a JSON as below from a web service
data = {
following = 1;
};
message = "You are now following";
status = 1;
and I am trying to loop it using the following code (in order to get the value of the "following" key)
for(NSDictionary *item in datarecieved){
int placeName = [item valueForKey:#"following"];
NSLog(#"FOLLOWING VALUE %i", placeName);
}
But I am getting an exception - "uncaught exception of type NSException"
You have to do like this
for(NSDictionary *item in datarecieved){
if([item class] == [NSDictionary class])
{
int placeName = [item valueForKey:#"following"];
NSLog(#"FOLLOWING VALUE %i", placeName);
}
}
because there are two other key in response and they dosen't contain following key
Try it
int placeName = [datarecieved[#"data"][#"following"] intValue];
Once you deserialize the JSON it will be NSDictionary, so you don't need to use a loop (unless you need to loop through all keys)
In your case if you just want value for following key, you can do the following
NSNumber *following = [datarecieved valueForKeyPath:#"data.following"];
NSLog(#"FOLLOWING VALUE %#", following);
First of all, you need valid JSON. Look into the OP comments, there is a good address for understanding JSON.
Then you first must parse the JSON data. Assuming datareceived is of class NSData:
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:datareceived options:nil error:&error];
if(error) {
NSLog(#"parsing error: %#", error);
}
else {
for(NSString *key in [dict allKeys]) {
NSLog(#"%# = %#", key, [dict objectForKey:key]);
}
}
The exception you are doubtless getting is unrecognized selector sent to instance 0x12345678 and is because item does not support that selector.
You need to check that item is actually an NSDictionary before calling valueForKey: (you should be using objectForKey: anyway):
for(NSDictionary *item in datarecieved){
if ([item isKindOfClass:[NSDictionary class]]) {
int placeName = [item objectForKey:#"following"];
NSLog(#"FOLLOWING VALUE %i", placeName);
}
}
(you probably want to break at some point once you've found what you're looking for as well).

[iOS][objC] unable to convert value in NSDictionary to NSString

I intend to convert the NSDictionary* object in iOS SDK to NSString*.
Lets say my NSDictionary object has following key value pairs:
{"aps":{"badge":9, "alert":"hello"}} (notice that value itself is a NSDictionary object)
and I want it to convert into a hash map with key value pair as {"aps":"badge:9, alert:hello"} (notice value is just a string).
I am able to print the value in NsDictionary using the following code:
NSDictionary *userInfo; //it is passed as an argument and contains the string I mentioned above
for (id key in userInfo)
{
NSString* value = [userInfo valueForKey:key];
funct( [value UTF9String]; // my function
}
But i am not able to call any NSString method on value object like UTT8String. It gives me error "Terminating app due to uncaught exception NSInvalidArgumentException: reason [_NSCFDictionary UTF8String]: unrecognised selector sent to instance
You are going to have to recursively process the dictionary structure, here is an example that you should be able to adapt:
-(void)processParsedObject:(id)object{
[self processParsedObject:object depth:0 parent:nil];
}
-(void)processParsedObject:(id)object depth:(int)depth parent:(id)parent{
if([object isKindOfClass:[NSDictionary class]]){
for(NSString * key in [object allKeys]){
id child = [object objectForKey:key];
[self processParsedObject:child depth:depth+1 parent:object];
}
}else if([object isKindOfClass:[NSArray class]]){
for(id child in object){
[self processParsedObject:child depth:depth+1 parent:object];
}
}
else{
//This object is not a container you might be interested in it's value
NSLog(#"Node: %# depth: %d",[object description],depth);
}
}
You need to apply that loop to each child, not the main dictionary. You said yourself you have a dictionary in a dictionary:
for(id key in userInfo)
{
NSDictionary *subDict = [userInfo valueForKey:key];
for(id subKey in subDict)
{
NSString* value = [subDict valueForKey:subKey];
}
}
This loop assumes you have the entire dictionary in the first level, otherwise you will need to use danielbeard's recursive method.
I found the easiest way out. calling the description method on the NSDictionary object gives me what I needed exactly. Stupid to have missed it on first go.

Resources