SBJson parse attempt -- objective C - iphone

I'm trying to parse the following JSon (which was validated I think last time I checked):
{
"top_level" = (
{
"download" = "http:/target.com/some.zip";
"other_information" = "other info";
"notes" = (
{
obj1 = "some text";
obj2 = "more notes";
obj3 = "some more text still";
}
);
title = "name_of_object1";
},
{
"download" = "http:/target.com/some.zip";
"other_information" = "other info";
"notes" = (
{
obj1 = "some text";
obj2 = "more notes";
obj3 = "some more text still";
}
);
title = "name_of_object2";
},
{
"download" = "http:/target.com/some.zip";
"other_information" = "other info";
"notes" = (
{
obj1 = "some text";
obj2 = "more notes";
obj3 = "some more text still";
}
);
title = "name_of_object3";
}
);
}
My attempt is using the following:
NSDictionary *myParsedJson = [myRawJson JSONValue];
for(id key in myParsedJson) {
NSString *value = [myParsedJson objectForKey:key];
NSLog(value);
}
Error:
-[__NSArrayM length]: unrecognized selector sent to instance 0x6bb7b40
Question:
It seems to me that JSon value makes myParsedJson object an NSArray instead of a NSDictionary.
How would I iterate through the objects called name_of_object and access each of the nested dictionaries? Am I going about it the right way?

The first argument of NSLog must be a string. Try this:
NSLog(#"%#", value);

Your value is not a string, just because you've typed it so.
Based on the structure you posted you will have an array as your
top-level object.
NSDictionary *myParsedJson = [myRawJson JSONValue];
for(id key in myParsedJson) {
id value = [myParsedJson objectForKey:key];
NSLog(#"%#", value);
}
The %# syntax in NSLog causes the -description method to be
called on value; this method returns an NSString. That means
that you could do NSLog([value description]); but this is
generally not a good idea. (Someone could craft malicious input that could crash your app.)

Related

Getting value from NSDictionary to NSMutableArray

I have this NSDictionary:
Categorys = {
Category = (
{
categoryDescription = {
text = " Description";
};
categoryIconPath = {
text = " 7.jpg";
};
categoryId = {
text = " 25";
};
categoryName = {
text = " My Photos";
};
categoryStatus = {
text = " ON";
};
publicPrivate = {
text = " Private";
};
userId = {
text = " 2";
};
},
..................
I want to get the categoryName (like here My Photos) into my NSArray.
How can I do this?
I am unable to get it
I have tried this code from http://troybrant.net/blog/2010/09/simple-xml-to-nsdictionary-converter/
NSString *category = [[[[xmlDictionary objectForKey:#"Categorys"] objectForKey:#"Category"] objectForKey:#"categoryName"] stringForKey:#"text"];
but it gives error
2012-03-27 15:30:35.320 File[1481:903] -[NSCFArray objectForKey:]: unrecognized selector sent to instance 0x1462c0
In Categories, the object referenced by the Category key is an array, you can tell by the fact that its contents are bracketed by parentheses ( ... ) not braces { ... }.
Looking at your code and breaking it down:
NSString *category = [[[[xmlDictionary objectForKey:#"Categorys"] // This is the outermost dictionary
objectForKey:#"Category"] // This is an NSArray
objectForKey:#"categoryName"] stringForKey:#"text"];
// This breaks NSArray doesn't respond to objectForKey:
Breaking it down a bit, you need:
NSDictionary* categorys = [xmlDictionary objectForKey:#"Categorys"];
NSArray* categoryArray = [categorys objectForKey: #"Category"];
NSDictionary* category = [categoryArray objectAtIndex: i]; // i is the index of the category you want e.g. 0
// etc
The answer is in the error message. One of the objects that you are getting from
NSString *category = [[[[xmlDictionary objectForKey:#"Categorys"] objectForKey:#"Category"] objectForKey:#"categoryName"] stringForKey:#"text"];
Doesn't have the selector objectForKey.
Now you need to break down your code and do some debugging to find out where you are going wrong.
Was getting the same error "[NSCFArray objectForKey:]: unrecognized selector sent to instance"
When I looked at the structure of my plist file that was loaded from the bundle at runtime, it did not seem possible that I was using the wrong selectors. The problem is that when I ran the app, it was not pulling the original plist from the bundle, it was pulling a saved plist from my apps directory. Because the saved plist had an incorrect, and completely different structure than my original plist, I indeed was using the wrong selectors.

App crashes when using allKeys, is it because there is only one object?

I can't figure out why xcode continues to throw this error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-
[__NSCFString allKeys]: unrecognized selector sent to instance 0x9367330'
whenever I use this code
NSDictionary *nextArray = [_classDictionary
valueForKeyPath:#"response.page_records.page_record"];
for (NSDictionary *roster2 in nextArray) {
NSArray *keys = [roster2 allKeys];
if ([keys containsObject:#"title"]){
[pageName addObject:[roster2 objectForKey:#"title"]];
}
if ([keys containsObject:#"id"]) {
[pageID addObject:[roster2 objectForKey:#"id"]];
}
}
After trying to debug it I found out that the exception is only thrown when there is only one object for the key. For example when "nextArray" is this:
(
{
"class_id" = 0001;
"id" = 1234;
"title" = "First Page";
},
{
"class_id" = 0002;
"id" = 12345;
"title" = Assignments;
}
)
the code runs without an exception, but when the code is simply
{
"class_id" = 0001;
"id" = 1234;
"title" = "First Page";
}
The exception is thrown. Why would this be happening? The keys still contains the object title, even if there is only one object. Any other ideas why this exception is being thrown? Does it have something to do with the parenthesis? (total guess) Is there a better way to debug what is really going on?
EDIT: Here is the _userDictionary. What is the best way to have an ordered array of the objectforKey: #"title" and avoid the above error?
response = {
"status" = ok;
"page_records" = {
"page" = 1;
"page_count" = 1;
"records_per_page" = 50;
"total_record_count" = 2;
"page_record" = (
{
"class_id" = 0001;
"id" = 1234;
"title" = "Test page";
},
{
"class_id" = 0002;
"id" = 12345;
"title" = "testing page 2";
}
);
};
};
}
In your first example, you are iterating through an array of dictionaries, and hence, NSArray *keys = [roster2 allKeys]; is being called upon an array. However, in your second example, you have just one dictionary. It is not an array of dictionaries. So your loop for (NSDictionary *roster2 in nextArray) is iterating through the keys of your dictionary, which are just strings, and not arrays. Hence, the error.
First of all, your naming is confusing. An NSDictionary named nextArray? Try this:
id *response = [_classDictionary valueForKeyPath:#"response.page_records.page_record"];
NSMutableArray *records;
if ([response isKindOfClass: [NSDictionary class]]) {
records = [[NSMutableArray alloc] init];
[records addObject: response];
}
else {
records = [NSMutableArray arrayWithArray: response];
}
for (NSDictionary *roster2 in records) {
NSArray *keys = [roster2 allKeys];
if ([keys containsObject:#"title"]){
[pageName addObject:[roster2 objectForKey:#"title"]];
}
if ([keys containsObject:#"id"]) {
[pageID addObject:[roster2 objectForKey:#"id"]];
}
}
The error message contains everything you need to know:
[__NSCFString allKeys]: unrecognized selector
It means that you're trying to call the method allKeys on an instance of NSString.
Let's look at where you call allKeys:
for (NSDictionary *roster2 in nextArray) {
NSArray *keys = [roster2 allKeys];
roster2 is an instance of NSString, not NSDictionary. You're expecting nextArray to contain NSDictionary objects, but it instead contains NSString objects.
No, it's because NSString doesn't respond to allKeys. The console log tells you this--you need to read it more carefully.
In NeXT-style plists, ({}) defines an array containing a dictionary. {} defines just a dictionary. That is, this:
{
"class_id" = 0001;
"id" = 1234;
"title" = "First Page";
}
Is not structurally the same as this:
(
{
"class_id" = 0001;
"id" = 1234;
"title" = "First Page";
}
)
Even though they contain the same information. Even if you have only one element in your root array, you still need to tell the plist parser that there is an array there, so you need to include ( and ) for your logic to handle it correctly.
Edit: Given the information in your comment, the solution will not be quite so simple. If your source data is arbitrary that way, you'll need to check the type of the data to see if it's an NSArray or NSDictionary, and handle it appropriately. For example:
id myData = [self plistFromArbitrarySource];
if (myData) {
if ([myData isKindOfClass: [NSArray class]]) {
for (NSDictionary *dict in myData) {
[self parseDictionary:dict];
}
} else if ([myData isKindOfClass: [NSDictionary class]]) {
[self parseDictionary:myData];
}
}

NSMutableArray and NSDictionary Error

I have one NSMutableArray called SubList that contains 262 element. Every object is a NSDictionary!
This code works like a charm!
for (NSDictionary *element in listSub){
[cell.textLabel setText:[element objectForKey:#"title"]];
}
But if I try to use use this code I get a SIGBART error!
NSDictionary * element = [listSub objectAtIndex:[indexPath row]];
[cell.textLabel setText:[element objectForKey:#"title"]];
So what's the problem?
*EDit the problem is at this line
NSDictionary * element = [listSub objectAtIndex:[indexPath row]];
and this is the description
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet objectAtIndex:]: unrecognized selector sent to instance 0x89a8d90'
Thanks
**Edit
This is the output of listSub!
listSub: {(
{
categories = (
{
id = "user/14761688561595773457/label/Web Programming";
label = "Web Programming";
}
);
firstitemmsec = 1265885595862;
id = "feed/http://feeds.feedburner.com/FamousBloggers";
sortid = A11327DE;
title = "Famous Bloggers";
},
{
categories = (
{
id = "user/14761688561595773457/label/Hacking";
label = Hacking;
}
);
firstitemmsec = 1241258413829;
htmlUrl = "http://backtrack-italia.blogspot.com/";
id = "feed/http://feeds2.feedburner.com/BackTrackItalia";
sortid = E8A04F76;
title = "Back Track Italia";
},
{
categories = (
{
id = "user/14761688561595773457/label/Hacking";
label = Hacking;
}
);
firstitemmsec = 1245376992188;
htmlUrl = "http://www.offensive-security.com";
id = "feed/http://www.offensive-security.com/blog/feed/";
sortid = 92F57555;
title = "BackTrack Information Security Distribution";
},
{
categories = (
{
id = "user/14761688561595773457/label/iOS Developer";
label = "iOS Developer";
}
);
firstitemmsec = 1296830392306;
htmlUrl = "http://amix.dk/Main/";
id = "feed/http://feeds.feedburner.com/amixdk";
sortid = 110C52C3;
title = "amix.dk blog";
},
{
categories = (
{
id = "user/14761688561595773457/label/Apple News";
label = "Apple News";
}
);
firstitemmsec = 1285261350202;
htmlUrl = "http://www.appletvhacks.net";
id = "feed/http://www.appletvhacks.net/feed/";
sortid = 81125D2E;
title = "Apple TV Hacks";
},
{
categories = (
{
id = "user/14761688561595773457/label/Apple News";
label = "Apple News";
}
);
firstitemmsec = 1293230300220;
htmlUrl = "http://www.appletvitalia.it";
id = "feed/http://www.appletvitalia.it/feed/";
sortid = 892FE61C;
title = "Apple Tv Italia";
},
{
categories = (
{
id = "user/14761688561595773457/label/Apple News";
label = "Apple News";
}
);
firstitemmsec = 1270115980935;
htmlUrl = "http://www.appleecious.com";
id = "feed/http://feeds2.feedburner.com/appleecious";
sortid = 00B5AFC2;
title = Appleecious;
},
{
categories = (
{
id = "user/14761688561595773457/label/Hacking";
label = Hacking;
}
);
firstitemmsec = 1258495136927;
htmlUrl = "http://www.rawseo.com/news";
id = "feed/http://www.rawseo.com/news/feed/";
sortid = D6766911;
title = "A blend of programming and seo";
},
{
categories = (
{
id = "user/14761688561595773457/label/Seo e Web Marketing";
label = "Seo e Web Marketing";
}
);
firstitemmsec = 1233684720758;
htmlUrl = "http://it-adsense.blogspot.com/";
id = "feed/http://it-adsense.blogspot.com/atom.xml";
sortid = 9FB570ED;
title = "AdSense Blog-Italiano";
},
{
categories = (
);
firstitemmsec = 1277627346000;
htmlUrl = "http://aext.net";
id = "feed/http://feeds.feedburner.com/aextnet";
sortid = 70800CFE;
title = "AEXT.NET NET MAGAZINE";
},
{
categories = (
);
firstitemmsec = 1217001547735;
htmlUrl = "http://www.alessandroscoscia.it";
id = "feed/http://feeds.feedburner.com/alessandroscoscia";
sortid = 51CB8E6E;
title = "Alessandro Scoscia";
},
{
categories = (
{
id = "user/14761688561595773457/label/iOS Developer";
label = "iOS Developer";
}
);
...
I'm sure that it isn't a NSSet.
Now I post the code that I'm using to populate my array.
NSError *error;
NSURLResponse *response;
NSData *dataReply = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];
NSString *string = [[NSString alloc] initWithData:dataReply encoding:NSASCIIStringEncoding];
NSLog(#"testing %#",string);
SBJsonParser *vol= [[SBJsonParser alloc]init];
if (string) {
NSArray *feeds = [vol objectWithString:string error:nil];
NSDictionary *results = [string JSONValue];
NSArray *subs = [results valueForKey:#"subscriptions"];
for (NSDictionary *iscrizione in subs){
[subscriptions addObject:iscrizione];
}
This is part of another class and also in this part of my code if i try to do something like this:
NSDictionary * element = [listSub objectAtIndex:0];
NSLog([element objectForKey:#"title"]);
I get the same error while if I try a loop like this
for (NSDictionary *element in listSub){
[cell.textLabel setText:[element objectForKey:#"title"]];
}
Everythings works fine without NSSet errors. This make me crazy!
The problem is probably that listSub was not properly retained and therefore released already. The memory is now re-used and the object at that address is now an __NSCFSet, which does not respond to objectAtIndex:.
Note that autoreleased objects returned by a convenience class method like [NSMutableArray arrayWithXXX] should be retained and only released when not needed anymore. The best way to be sure they are retained is to declare a #property(retain) NSArray *subList, #synthesize it and use self.subList in all cases except perhaps in dealloc.
Addition
It was suggested that the method that created subList returned a set and not an array. If that were the case, the simple loop at the begining of the answer would not function either. That is why I find it very unlikely that subList was originally an NSSet, and much more likely that the array was overreleased and dealloced and the memory re-used for the NSSet.
Please do what people who want to help you ask of you.
Add the line
NSLog(#"listSub: %#", listSub);
at the place where listSub is set and also right before the error line. Then look in the error log what it displays and tell us.
Have you tried:
[cell.textLabel setText:[element objectForKey:#"title"]];
What is that "lol"??
listSub isn't an array, it's an NSSet.
problem is here
[cell.textLabel setText:[NSString stringWithFormat:[element objectForKey:#"title"]]];
when you are call stringWithFormat then you need to give format in string literal form like this
[cell.textLabel setText:[NSString stringWithFormat:#"%#",[element objectForKey:#"title"]]];
or direct set the text with out calling stringWithFormat:
your array got released some where, so for solving the problem you need to call retain on tyour array at right place.
Just try this one.
NSDictionary * element = [listSub objectAtIndex:[indexPath row]];
[cell.textLabel setText:[NSString stringWithFormat:#"%#",[element objectForKey:#"title"]]];
Or try this as you have edit your question, Edit
NSDictionary *element = [[NSDictionary alloc] initWithDictionary:[listSub objectAtIndex:indexPath.row]];
[cell.textLabel setText:[element objectForKey:#"title"]];
[element release];

Trouble reading JSON object using Obj-C

I am trying to read the following json object using the json-framework and obj-C
{
Sections = {
Now = "Wednesday 9 February 2011 02:40";
Section = (
{
Article = (
{
Exceprt = "text here";
ID = 49011;
Title = "text here";
Type = Politics;
audioCounter = 0;
commentsCounter = 0;
hasMore = false;
important = False;
likesCounter = 0;
photoCounter = 0;
time = "21:12";
timeStamp = "2/8/2011 9:14:16 PM";
timeStatus = True;
videoCounter = 0;
viewsCounter = 0;
},
{
Exceprt = "text here";
ID = 49010;
Title = "text here";
Type = Politics;
audioCounter = 0;
commentsCounter = 0;
hasMore = false;
important = True;
likesCounter = 0;
photoCounter = 0;
time = "20:45";
timeStamp = "2/8/2011 9:10:59 PM";
timeStatus = True;
videoCounter = 0;
viewsCounter = 0;
},
{
Exceprt = "text here";
ID = 49008;
Title = "text here";
Type = Politics;
audioCounter = 0;
commentsCounter = 0;
hasMore = false;
important = False;
likesCounter = 0;
photoCounter = 0;
time = "20:28";
timeStamp = "2/8/2011 9:09:44 PM";
timeStatus = True;
videoCounter = 0;
viewsCounter = 0;
}
);
ID = 22;
Name = "EN Live";
totalNews = 3416;
}
);
};
}
My intent is to have a list of the articles (list of dictionaries) so that I can later access them easily. I have been stuck a while on this and my code is giving me an error about calling a non existent method for NSArray which has led me to suspect that I am misunderstanding the json object. I am totally new to this and any help is greatly appreciated.
Here's my code:
NSDictionary *results = [jsonString JSONValue];
NSDictionary *Articles = [[results objectForKey:#"Sections"] objectForKey:#"Section"];
NSArray *ListOfArticles = [Articles objectForKey:#"Article"];
for (NSDictionary *article in ListOfArticles)
{
NSString *title = [article objectForKey:#"Title"];
NSLog(title);
}
Thanks !
First of all, those aren’t valid JSON data. Names (in name/value pairs) are strings and must be quoted. String values must always be quoted. Boolean values must be either true or false (lowercase). Check http://json.org/ and http://www.ietf.org/rfc/rfc4627.txt?number=4627 and http://jsonlint.com
Here’s the structure of your data:
The top level value is an object (dictionary)
This object has a name (key) called Sections whose value is itself another object (dictionary)
Sections has a name (key) called Section whose value is an array
Each element in the Section array is an object (dictionary)
Each element in the Section array has a name (key) called Article whose value is an array, as well as other names (keys): ID, title, totalNews
Each element in the Article array is an object
If your JSON data were valid, you could parse them as follows:
// 1.
NSDictionary *results = [jsonString JSONValue];
// 2.
NSDictionary *sections = [results objectForKey:#"Sections"];
// 3.
NSArray *sectionsArray = [sections objectForKey:#"Section"];
// 4.
for (NSDictionary *section in sectionsArray) {
// 5.
NSLog(#"Section ID = %#", [section objectForKey:#"ID"];
NSLog(#"Section Title = %#", [section objectForKey:#"Title"];
NSArray *articles = [section objectForKey:#"Article"];
// 6.
for (NSDictionary *article in articles) {
NSLog(#"Article ID = %#", [article objectForKey:#"ID"];
NSLog(#"Article Title = %#", [article objectForKey:#"Title"];
// …
}
}
Your JSON framework is probably parsing out an NSDictionary where you're expecting an NSArray. It'll let you assign an NSDictionary to an NSArray, but then you'll get a runtime exception when you attempt to call a method on your "array". Judging by the JSON you posted (which isn't correct JSON), this is what I would have my parsing code look like. The names of the NSDictionaries and NSArrays are simply named after the JSON attributes they represent.
NSDictionary* results = [jsonString JSONValue];
NSDictionary* sections = [results valueForKey:#"Sections"];
NSArray* section = [sections valueForKey:#"Section"];
NSArray article = [[section objectAtIndex:0] valueForKey:#"Article"];
for (NSDictionary* anArticle in article) {
NSLog(#"%#", [anArticle valueForKey:#"Title"]);
}

Copying a dictionary with multiple sub dictionaries and only returning certain keys from the sub dictionaries

In my current iPhone project, I have a created a dictionary that groups the sub dictionaries by the first letter of the "Name" key. NSLog returns the following. I would like to create an identical dictionary that only shows the "Name" key under each initial letter key. What is the best way for making a copy of some of the items in the sub dictionaries? The ObjectForKey methods will select only the items for the initial letter (ex: "B" or "C"). Please let me know if I didn't explain this clearly enough.
Thanks!
sectionedDictionaryByFirstLetter:{
B = (
{
Name = "B...A Name Starting with B";
Image = "ImageName1.png";
Text = "Some Text";
}
);
C = (
{
Name = "C...A Name Starting with C";
Image = "ImageName2.png";
Text = "Some Text";
}
);
N = (
{
Name = "N...A Name Starting with N";
Image = "ImageName3.png";
Text = "Some Text";
},
{
Name = "N...A Name Starting with N";
Image = "ImageName4.png";
Text = "Some Text";
},
{
Name = "N...A Name Starting with N";
Image = "ImageName5.png";
Text = "Some Text";
}
);
}
The final result I'm looking for is:
sectionedDictionaryByFirstLetter:{
B = (
{
Name = "B...A Name Starting with B";
}
);
C = (
{
Name = "C...A Name Starting with C";
}
);
N = (
{
Name = "N...A Name Starting with N";
},
{
Name = "N...A Name Starting with N";
},
{
Name = "N...A Name Starting with N";
}
);
}
Core Foundation has a great function, CFDictionaryApplyFunction (I wish they ported its functionality to NSDictionary too). However, NSDictionary and CFDictionary are "toll-free bridges", meaning that you can cast between them at no cost.
So, the solution would be to create an applier function, SaveName, which would be used in the CFDictionaryApplyFunction above. Example:
void SaveName(const void* key, const void* value, void* context) {
NSMutableDictionary* result = (NSMutableDictionary*) context;
NSDictionary* dataDict = (NSDictionary*) value;
NSString* letterKey = (NSString*) key;
[result setObject:[dataDict valueForKey:#"Name"] forKey:#letterKey];
}
void main() {
NSDictionary* exampleDict = .. ;
NSMutableDictionary* resultDict = [NSMutableDictionary dictionary];
CFDictionaryApplyFunction((CFDictionaryRef)exampleDict, SaveName, (void*)resultDict);
// now, the resultDict contains key-value pairs of letter-name!
}
My code assumes that there is only one object in the "value" dictionary. You can change SaveName to take into consideration your own data structure, but that's basically the way to do it.
NSMutableDictionary* newDict = [NSMutableDictionary dictionary];
for (NSString* key in sectionedDictionaryByFirstLetter) {
NSMutableArray* newList = [NSMutableArray array];
[newDict setObject:newList forKey:key];
for (NSDictionary* entry in [sectionedDictionaryByFirstLetter objectForKey:key]) {
NSString* name = [entry objectForKey:#"Name"];
[newList addObject:[NSDictionray dictionaryWithObject:name forKey:#"Name"]];
}
}