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

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.

Related

Is there a way to acquire the properties from this obscure object and convert it to JSON?

I'm working with a remote library that is delivering this object to me.
If I set my debugger, I get this information :
KandyChatMessage: UUID - 70886A79-2FF60F5E1A3961EF , timestamp - 2017-02-13 17:46:12 +0000 , sender - uri - 3#domain.domain.com, userName - 3, domain - domain.domain.com, type - 0, associationType - 1 , recipient - uri - afdab3bfb5774#domain.domain.com, userName - afdab3bfb57a12b5, domain - domain.domain.com, type - 1, associationType - 1 , type: - 1 , mediaItem - KandyTextMessageData - text:3 , info:(null) , isIncoming - 1 , additionalData - (null), fromHistory - NO
It is delivered via this method :
-(void)_addEventAndRefresh:(id<KandyEventProtocol>)event{
The goal is to convert this object into JSON with something like this :
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:event
options:0
error:&error];
However, this crashes my app as I assume that event doesn't fulfill all the rules of a serializable NSMUtableArray or a NSDictionary for some reason.
This leaves me with two options. One, I can find some crafty method to convert whatever object this is into JSON. Or two, I can cherry-pick its data out and write an NSDictionary Object from scratch.
Would anyone have the slightest clue on how to pick this kind of object apart?
In my debugger, it doesn't seem to respond to anything..
> po event.UUID
=> error: property 'UUID' not found on object of type 'id'
> po event.timestamp
=> error: property 'timestamp' not found on object of type 'id'
You're right that the object is probably not serializable as JSON because it doesn't qualify in one or all of these ways. In a nutshell, it must be a collection of strings, numbers and other collections, and nothing else.
This answer suggests a way to get properties if you know the class. In a nutshell, it relies on objc_property_t *properties = class_copyPropertyList(klass, &outCount); to get the properties.
You can use that to get the object's JSON serializable properties, placing their values (keyed by their name) in a dictionary. Then JSON serialize that.
EDIT
To demonstrate, I added this method to the PropertyUtil class suggested by that other answer...
+ (id)jsonSerializableFromObject:(id)object {
if ([object isKindOfClass:[NSString self]] ||
[object isKindOfClass:[NSNumber self]] ||
[object isKindOfClass:[NSNull self]]) {
return object;
} else if ([object isKindOfClass:[NSArray self]]) {
NSMutableArray *result = [NSMutableArray array];
for (id e in (NSArray *)object) {
id element = [self jsonSerializableFromObject:e];
[result addObject:element];
}
return result;
} else if ([object isKindOfClass:[NSArray self]]) {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (id key in [(NSDictionary *)object allKeys])
result[key] = [self jsonSerializableFromObject:(NSDictionary *)object[key]];
return result;
} else {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSDictionary *props = [PropertyUtil classPropsFor:[object class]];
for (NSString *propName in [props allKeys]) {
id value = [object performSelector:NSSelectorFromString(propName)];
result[propName] = [self jsonSerializableFromObject:value];
}
return (result.count)? result : [NSNull null];
}
}
Just a quick sketch here: if the operand can be serialized to JSON, return it. If the operand is an array, answer an array of each element made json-serializable. Same if it's it a dictionary. Otherwise, if the object is something arbitrary, apply the introspection method to get it's properties, invoke the getter for each property and answer the json-serializable of that.
So I managed to get a list of all methods like so :
int i=0; unsigned int mc = 0;
Method * mlist = class_copyMethodList(event, &mc);
NSLog(#"%d methods", mc); for(i=0;i<mc;i++)
NSLog(#"Method no #%d: %s", i, sel_getName(method_getName(mlist[i])));
And then I realized I had to class cast the object to retrieve its items :
KandyChatMessage *chatMessage = (KandyChatMessage*)event;
chatMessage.description;

-[__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.

Get A Certain Value In NSDictionary

Let's say the name of dictionary is nameOfDictionary and the key name is string.
I was trying to get the value of the dictionary by using:
[nameOfDictionary objectForKey:#"string"];
However, I was getting the one at the bottom. Is there anyway I can get the 222 or sample alone?
{
222 = "sample";
}
It appears that the object under the "string" key is also a dictionary. So you can do this to get the key and value of that dictionary separately.
NSDictionary *nameOfDictionary = #{#"string":#{#"222":#"Sample"}};
NSDictionary *dict = nameOfDictionary[#"string"];
for (NSString *key in [dict keyEnumerator]) {
NSLog(#"%# = %#", key, dict[key]);
}
key will contain 222 and dict[key] will contain sample
Try allKeys function will return all keys,
for( NSString *aKey in [dictionary allKeys])
{
NSLog(#"Do something with the key here:%#",aKey );
}

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).

Unrecognized Selector on Calling allKeys

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.
}

Resources