Getting value from NSDictionary to NSMutableArray - iphone

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.

Related

SBJson parse attempt -- objective C

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

Adding values into my plist

I have this plist that I have created
I have written most of my controller class which gets this plist and loads it into the documents directory so its possible to read/write to is.
Currently I have the reading working fine, and I used to have the writing working also, however I have just recently changed one of the objects (cache value) to a Dictionary with values related to that. Now when I try to write to this plist my app is crashing.
This is the error I am getting.
2012-04-05 09:26:18.600 mycodeTest[874:f803] * Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: '*
-[NSDictionary initWithObjects:forKeys:]: count of objects (4) differs from count of keys (5)'
*** First throw call stack: (0x12cc022 0x1884cd6 0x1248417 0x12685e2 0x19844 0x17e86 0x17669 0x13b67 0xe53a49 0xe51e84 0xe52ea7 0xe51e3f
0xe51fc5 0xd96f5a 0x1d2aa39 0x1df7596 0x1d21120 0x1df7117 0x1d20fbf
0x12a094f 0x1203b43 0x1203424 0x1202d84 0x1202c9b 0x21aa7d8 0x21aa88a
0x450626 0x77ed 0x1e35 0x1) terminate called throwing an
exceptionCurrent language: auto; currently objective-c
with all of this in mind I will now show you my method, which is called from another class when it has the values ready to be saved.
//This method gets called from another class when it has new values that need to be saved
- (void) saveData:(NSString *)methodName protocolSignature:(NSString *)pSignature protocolVersion:(NSNumber *)pVersion requestNumber:(NSNumber *)rNumber dataVersionReturned:(NSNumber *)dvReturned cacheValue:(NSMutableDictionary *)cValue
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// set the variables to the values in the text fields that will be passed into the plist dictionary
self.protocol = pSignature;
self.Version = pVersion;
self.request = rNumber;
self.dataVersion = dvReturned;
//if statment for the different types of cacheValues
if (methodName == #"GetMan")
{
//cache value only returns the one cachevalue depending on which method name was used
[self.cacheValue setValue:cValue forKey:#"Man"]; //do I need to have the other values of cacheValue dictionary in here? if so how do I do that.
c
}
else if (methodName == #"GetMod")
{
[self.cacheValue setValue:cValue forKey:#"Mod"];
}
else if (methodName == #"GetSubs")
{
[self.cacheValue setValue:cValue forKey:#"Subs"];
}
// This is where my app is falling over and giving the error message
// create dictionary with values in UITextFields
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects: protocol, pVersion, rNumber, dvReturned, cacheValue, nil] forKeys:[NSArray arrayWithObjects: #"Signature", #"Version", #"Request", #"Data Version", #"Cache Value", nil]];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
NSString *myString = [[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding];
NSLog(#"%#", myString);
}
else
{
NSLog(#"Error in saveData: %#", error);
// [error release];
}
}
I am abit lost when the error is saying that 4 keys differ from 5 when as far as i can tell i am applying 5 values to the dictionary any help would be appreciated.
Edit** another thing I noticed when debugging my issues was the fact it looks like I am not getting my cacheValue dictionary set up properly as its showing 0 key valuepairs??? is this right or wrong?
this is what happens when I log my plist in xcode as suggested below when I use [NSDictionary dictionaryWithObjectsAndKeys:..etc
Check setup is everything there?
Temp Dic output = {
Root = {
"Cache Value" = {
Manu = 0;
Mod = 0;
Sub = 0;
};
"Data Version returned" = 0;
"Signature" = null;
"Version" = 0;
"Request Number" = 0;
};
Run Man cache check results
Temp Dic output = {
"Version returned" = 5;
"Signature" = Happy;
"Version" = 1;
"Request Number" = 4;
as you can see Cache Value is completely missing after I have run the request.
I'm going to guess that cacheValue is nil when the crash occurs, resulting in only 4 objects in your values array, but 5 in keys.
Try using [NSDictionary dictionaryWithObjectsAndKeys:] instead.
In a situation like this, break up your code. Do each piece on a separate line, with temporary variables.
Put your keys and your values into temporary arrays.
Lot the values of everything, or set breakpoints in the debugger and examine all your values. Eli is almost certainly right that cacheValue is nil. The arrayWithObjects method stops on the first nil.
This code:
NSString *string1 = #"string 1";
NSString *string2 = #"string 2";
NSString *string3 = #"string 3";
NSString *string4 = nil;
NSString *string5 = #"string 5";
NSArray *anArray = [NSArray arrayWithObjects:
string1,
string2,
string3,
string4,
string5,
nil];
NSLog(#"anArray has %d elements", [anArray count]);
Will only show 3 elements in the array, even though the arrayWithObjects line appears to add 5 elements

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];
}
}

Help needed to parse json for iPhone

I want to parse a JSON file in my iphone app. The problem is i can parse simple json files but i am confused how to do parsing on following type of json:
[{"123":
[{ "item_id":"222",
"image_count":"2",
"image_filetype":".jpg",
"image_url":"http:\/\/someurl.jpg",
},
{"item_id":"333",
"image_count":"2",
"image_filetype":".jpg",
"image_url":"http:\/\/someurl.jpg",
}]
}]
Can some on help me how to extract all the img_urls for "123".
Thank you.
NSString *jsonString = …;
// The top-level object is an array
NSArray *array = [jsonString JSONValue];
// The first element in the array is an object containing a name-value
// pair for the key/name "123". The value is itself an array
NSArray *itemsIn123 = [[array objectAtIndex:0] objectForKey:#"123"];
// Use Key-Value Coding to get an array of all values for the key
// image_url
NSArray *imgurls = [itemsIn123 valueForKey:#"image_url"];
Edit based on comments:
Since the top-level array may consist of several objects, each object having a single name-value pair with unknown name, you need to manually iterate over the top-level array:
NSString *jsonString = …;
NSMutableArray *imgurls = [NSMutableArray array];
// The top-level object is an array
NSArray *array = [jsonString JSONValue];
// Each element in the top-level array is an object
for (NSDictionary *outerObject in array) {
// Iterate over all values in the object. Each (single) value is an array
for (NSArray *innerArray in [outerObject allValues]) {
[imgurls addObjectsFromArray:[innerArray valueForKey:#"image_url"]];
}
}
The value for the object "123" will be an NSArray of NSDictionaries. Each of these dictionaries has a key "image_url" for the image url.
The code will depend on which JSON parsing library you use, but the basics should be the same.
First you want to take the key values like 123,112,189 so we will take the keys into an array
say the structure like [ Web { 123 {image url} 112 {image url} 189 {image url} ]
so
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
SBJSON *jsonParser = [SBJSON alloc]init];
NSMutableArray *yourArray1 = [jsonParser objectWithString:responseString]copy]]autorelease;
ufArray = [[yourArray1 valueForKey:#"web"] copy];
for (NSString *s in ufArray) {
[keys addObject:[NSDictionary dictionaryWithObjectsAndKeys:s,#"keys",nil]];
}
NSLOG(#"keys :%#",keys);
// this will contain 112,123,114 etc values
initialize a NSMutableArray
finalArray = [NSMutableArray alloc]init];
for (int i = 0; i < [ufArray count]; i ++) {
yourArray1 = [ufArray valueForKey:[[keys objectAtIndex:i]valueForKey:#"keys"]];
// [keys object at indes:i] - > 123 val / next loop 112 array like that
[finalArray addObject:yourArray1];
}
[jsonParser release];
jsonParser = nil;
Hope this helps!
Well if that array was called jArray
var img_urls = [];
var jL = jArray[0][123].length;
var img_urls = [];
for(var i = 0; i < jL; i++){
img_urls[i] = jArray[0][123][i].image_url;
}
//display in console:
console.log(img_urls);
demo: http://jsfiddle.net/maniator/Vx3hu/4/
I've never used JSON before, never used iPhone before, never used Xcode before...but I would think its something along along the lines of...
//object and image for item ID 222
123: item_id(222).image_url("some_url")
or the second and following items
//hi
123: item_id(333).image_url("some_url")
However something better would be when you can extract the image without the URL by using the item ID and an image ID, so when calling the object 123, you can specify the item id and the image id, which would then output all the information you require. For instance the count, file type and the image could all be displayed.
123: item_id(222).image_id(222)
Is the data file SQL or XML? XML is usually faster! So read up on nodes.
Hope that helps.
DL.

MGTwitterEngine and iPhone

I downloaded MGTwitterEngine and added to my iPhone project. It's connecting and getting statues I can tell from dumping them into an NSLog. But, I can't figure out how how I need to parse the calls so I can add them to a table. They are returned as an NSString and look like this:
{
"created_at" = 2009-07-25 15:28:41 -0500;
favorited = 0;
id = 65;
source = "Twitter";
"source_api_request_type" = 0;
text = "The wolf shirt strikes again!! #sdcc :P http://twitpic.com/blz4b";
truncated = 0;
user = {
"created_at" = "Sat Jul 25 20:34:33 +0000 2009";
description = "Host of Tekzilla on Revision3 and Qore on PSN. Also, a geek.";
"favourites_count" = 0;
"followers_count" = 0;
following = false;
"friends_count" = 0;
id = 5;
location = "San Francisco";
name = "Veronica Belmont";
notifications = false;
"profile_background_tile" = false;
"profile_image_url" = "http://blabnow.com/avatar/Twitter_10350_new_twitter_normal.jpg";
protected = 0;
"screen_name" = Veronica;
"statuses_count" = 2;
"time_zone" = UTC;
url = "http://www.veronicabelmont.com";
"utc_offset" = 0;
};
Anybody used this that can tell me how everyone else uses it in their project?
Thanks
What you are seeing in your console is an NSLog of an NSDictionary and not an NSString. From Matt Gemmell's MGTwitterEngine Readme:
The values sent to these methods are all NSArrays containing an NSDictionary for each status or user or direct message, with sub-dictionaries if necessary (for example, the timeline methods usually return statuses, each of which has a sub-dictionary giving information about the user who posted that status).
So whatever object you passed to your NSLog() statement is actually a dictionary and you can access the fields with a call to:
NSString *createdAtDate = [record valueForKey:#"created_at"];
NSString *source = [record valueForKey:#"source"];
// etc...
Where record is the object. Keep in mind that the user field is a sub-dictionary. You access it this way:
NSDictionary *userDict = [record valueForKey:#"user"];
NSString *name = [userDict valueForKey:#"name"];
NSString *location = [userDict valueForKey:#"location"];
// etc...
You could actually use the NSArray returned in the request as your table view's data source and then just extract the one you need by the index in your -cellForRowAtIndexPath table view delegate.
Best Regards,
For anyone else who might find their way here, here's one way to parce the results. (From a newbie, so don't count of this being the standard or even correct way)
The key (pun intended :D) is to use the dictionary in the appropriate delegate method. Check out Matt Long's example code in another thread on the topic.
To parce something like this:
[myTwitterEngine getSearchResultsForQuery:#"#ironsavior"];
His example is this, in the delegate method:
- (void)searchResultsReceived:(NSArray *)searchResults
forRequest:(NSString *)connectionIdentifier
{
if ([searchResults count] > 0)
{
NSDictionary *result = [searchResults objectAtIndex:0];
NSString *fromUser = [result valueForKey:#"from_user"];
NSString *fromUserID = [result valueForKey#"from_user_id"];
// ...
NSString *text = [result valueForKey#"text"];
NSLog(#"User %#(%#): %#", fromUser, fromUserID, text);
}
}
This would give you a very simple message that goes "User username(userid): message".
I'm not sure what the best way to proceed would be, I'm thinking returning a dictionary or an array that you could use elsewhere in your implementation. Or just return the original array and parse it elsewhere.
Check out that other thread for more info.