I have a NSDictionary that I am trying to get some strings out of. The name of the dictionary is called tweets. I can get the user name and tweet by doing the following.
NSDictionary *tweet = self.detailItem;
NSString *username = [[tweet objectForKey:#"user"] objectForKey:#"name"];
NSString *tweetText = [tweet objectForKey:#"text"];
If I NSLog the results I get; username is "person's username" and tweetText is "person's tweet." These are both NSString that I can manipulate.
However, I also need to get the actual URL from the tweet, not the shortened one. So I have found it in the dictionary with valueForKeyPath. I need this as a string and have tried the following.
NSString *url = [tweet valueForKeyPath:#"entities.urls.display_url"];
I can NSLog this as well and I get the proper information. But I think it comes out as an Array? The NSLog looks like this.. url is ( "url goes here" ). With the brackets and quotes. This is not a string like the others. I am assuming it is because of the valueForKeyPath. When I tried to use url hasPrefix, it doesn't like that and mentions you can't have hasPrefix with and array. But I also tried to use multiple objectForKey with entities, urls, and display_url. But this does not work. Is there any way to get my entry into a string format. I have looked over some SO questions and on the web, but can't find anything that is useful for me. Any help will by much appreciated. Thanks for your time
The keyPath entities.urls.display_url traverse an array of arrays and flattens that to return an array of display_urls. If you were confident that there would be only one url within that hierarchy you could use
NSString *url = [[tweet valueForKeyPath:#"entities.urls.display_url"] lastObject];
If there are multiple entities with URLs you may have to traverse the entities and urls to determine the URL in which you are interested. Or you may be able to use firstObject if what you are really looking for is entities.urls.[0].display_url
Per Twitter's documentation on entities, entities is a dictionary and urls is an array of dictionaries.
When you perform ... valueForKeyPath:#"entities.urls.display_url"] you do the same thing as ... valueForKey:#"entities"] valueForKey:#"urls] valueForKey:#"display_url"].
Per NSDictionary's documentation, its valueForKey: returns the same thing as objectForKey: if — as in this case — the key in question doesn't start with an #.
So the entities part of valueForKeyPath: returns an array of objects.
Per NSArray's documentation, its valueForKey: returns an array comprised of the result of calling valueForKey: on each object in the array individually.
Given that each thing in the entities array is a dictionary, what you therefore get back is an array of the display_url key for every entity in the tweet.
So I think your problem is that you expect to get "the actual URL from the tweet" (emphasis added). Tweets may contain arbitrary many links outward, not merely one — entities represent ranges of characters with special meanings like links, hashtags and similar. So you end up with an array rather than a single value.
Related
I know this question had been asked for several times, but I have trouble reading data in Xcode iOS. The problem was to achieve an implementation without having the keys for NSDictionaries hardcoded in the code.
Let's say, we have this .plist:
This is my plist
In this case I want to access the name and address from the restaurants dynamically without accessing the data by a hardcoded string.
Background of this is that I try to access the data on every cell on a grouped UITableView for iOS.
Anyone has some good information about this?
Generally (normally I code in c#) I know how to access dictionaries. But in this case, there are nested dictionaries with arrays which get me really confused and also frustrating :(
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyListName" ofType:#"plist"];
//Plist should exist in main bundle
NSDictionary *root = [[NSDictionary alloc] initWithContentsOfFile:path];
Then in your case your first value is a array in dictionary read that object in an array like,
NSArray * restaurantsArray = [names objectForKey:#"restaurants"];
Then so forth You know what type of object exist in the hierarchy just call respective methods of each,
like objectForKey for dictionary.
objectAtIndex for array.
This is the link which I have to parse it in Objective C:
http://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exsentences=2&exintro=&titles=USA
If you clearly look at the link the word "USA" is actually going to change as per user requests.In the JSON information after pages key you can actually see a number before pageid. How can I access it? If that field is static I can access with that particular key. But it seems like that number is dynamic according to the user search. My aim is to access "extract" key. If I want to do that I have to go inside to that number which is a dynamic one. I appreciate any kind of help. Thanks in advance.
Under "pages" you are getting a dictionary with keys that are the same as the ids (just strings). It seems your question circles around the fact that you do not know these keys beforehand.
Once you extracted the pages dictionary, you can iterate through the keys like this:
for (NSString *idString in pages.allKeys) {
NSDictionary *content = pages[idString];
NSString *extract = content["extract"];
// do something with extract
}
If you just want the any key or you are sure there is only one, you can instead create the content dictionary with
NSDictionary *content = pages[pages.allKeys.firstObject];
One expression doing the same as Mundi's answer:
NSString *extract =
[[[array valueForKeyPath:#"query.pages"] allObjects].firstObject
valueForKey:#"extract"];
Say you do this,
NSString *teste = yourData[#"title"];
no problem if "title" is completely missing in the json: you just get null. If you do this:
NSString *teste = yourData[#"location"][#"city"];
if "city" is missing in the json nest, no problem. if the whole "location" section does not exist, again no problem
However! You'll often see json like this, " largeImage = "<null>"; "
In that case, the app will crash if you are using the code above.
In practice you have to do this:
NSString *imageUrl = nil;
if ([yourResults[thisRow][#"largeImage"] isKindOfClass:[NSDictionary class]])
imageUrl = yourResults[thisRow][#"largeImage"][#"URL"];
My question was really:
is there some dead clever way to perhaps override the literal syntax (ie, override the underlying message, perhaps??) to cover this problem?
So essentially, make this concept [#"blah"] basically first check that indeed it is a dictionary at hand, before trying the operation.
It's a shame because, effectively, you can never use this wonderful syntax
yourData[#"location"][#"city"]
in practice, due to the problem I outline.
PS sorry for the earlier confusion on this question, fixed by Paramag. below - good one Paramag.
Personally i use with JSON category which returns null instead NSNull so my code looks:
[[json objectForKeyNotNull:#"Key"] objectForKeyNotNull:#"Other"]
As you want to have code shorter, i think i would create the category on NSDictionary which could be used as :
[json objectForPath:#"Key.Value"]
Which would expand the path into the keys.
There is some nice gist which looks like it's doing it:
https://gist.github.com/Yulong/229a62c1188c3c024247#file-nsdictionary-beeextension-m-L68
to check this kind of null , you can use valueForKeyPath:
NSString *teste = [CLOUD.yourData[thisRow] valueForKeyPath:#"location.city"];
it will first check for "location" and then for "city".
I would say this is a problem with your schema. null in JSON and a dictionary (called an "object") in JSON are different types of things. The fact that your key can have a value that is sometimes null and sometimes a dictionary seems to me that you guys are not following a rigorous schema.
A good design would have the key either not be there, or if it is there, its value is guaranteed to be a dictionary. In such a case, the Objective-C code NSString *teste = yourData[#"location"][#"city"]; would work without modification because if the key "location" didn't exist, then its value would be nil in Objective-C, and subsequent accesses won't crash, and will also return nil.
Using Restkit in my iOS project and the api is getting back a generic array of strings. How do I get access to those array of strings?
This is what is returned by the api.
{
"servers": ["http://myserver.com", "http://myotherserver.com"]
}
Assuming the response is an NSDictionary:
strings = [{response_object} objectForKey: #"servers"]
Will return an NSArray of the strings held in the servers key.
Replace {response_object} with whatever your response is. I may not be understanding your question though, it's quite vague.
I am reading on objective-c (a nerd ranch book), and I can't help thinking about this question: How do I decide which collection type, NSArray or NSDictionary (both with or w/o their mutable subclasses), to use when reading content from URL?
Let's say am reading JSON data from a PHP script (a scenario am dealing with), which to use? I know it is stated in many references that it depends on structure of data (i.e. JSON), but could a clear outline of the two structures be outlined?
Thank you all for helping :)
NSArray is basically just an ordered collection of objects, which can be accessed by index.
NSDictionary provides access to its objects by key(typically NSStrings, but could be any object type like hash table).
To generate an object graph from a JSON string loaded via a URL, you use NSJSONSerialization, which generates an Objective-C object structure. The resulting object depends on the JSON string. If the top-level element in your JSON is an array (starts with "["), you'll get an NSArray. If the top-level element is a JSON object (starts with "{"), you'll get an NSDictionary.
You want to use NSArray when ever you have a collection of the same type of objects, and NSDictionary when you have attributes on an object.
If you have, lets say a person object containing a name, a phone number and an email you would put it in a dictionary.
Doing so allows the order of the values to be random, and gives you a more reliable code.
If you want to have more then one person you can then put the person objects in an array.
Doing so allow you to iterate the user objects.
"withContentOfURL" or "withContentOfFile" requires the data in the URL or the file to be in a specific format as it is required by Cocoa. JSON is not that format. You can only use these methods if you wrote the data to the file or the URL yourself in the first place, with the same data. If you write an NSArray, you can read an NSArray. If you write an NSDictionary, you can read an NSDictionary. Everything else will fail.