My application has an NSDictionary containing many other NSDictionary inside it. If I print out this dictionary it reads as follows:
oxip = {
created = "2014-02-10 14:42:59";
lastMsgId = "";
requestTime = "1.6434";
response = {
code = 001;
debug = "";
message = success;
request = getHierarchyByMarketType;
text = "\n";
williamhill = {
class = {
id = 1;
maxRepDate = "2014-02-10";
maxRepTime = "07:31:48";
name = "UK Football";
text = "\n";
type = (
{
id = 2;
lastUpdateDate = "2013-12-26";
lastUpdateTime = "13:32:54";
market = (
{
betTillDate = "2014-02-15";
betTillTime = "15:00:00";
date = "2014-02-15";
id = 140780553;
lastUpdateDate = "2014-02-10";
lastUpdateTime = "14:09:13";
name = "Queen of the South v Dundee - Match Betting";
participant = (
{
handicap = "";
id = 496658381;
lastUpdateDate = "2014-02-10";
lastUpdateTime = "14:09:13";
name = Dundee;
odds = "11/8";
oddsDecimal = "2.38";
text = "\n\n\n\n\n\n";
},
{
handicap = "";
id = 496658380;
lastUpdateDate = "2014-02-10";
lastUpdateTime = "14:09:13";
name = Draw;
odds = "5/2";
oddsDecimal = "3.50";
text = "\n";
},
{
handicap = "";
id = 496658379;
lastUpdateDate = "2014-02-10";
lastUpdateTime = "14:09:13";
name = "Queen of the South";
odds = "11/8";
oddsDecimal = "2.38";
text = "\n";
}
);
text = "\n";
time = "15:00:00";
}
What is the best possible way for my application to reach the NSDictionary with the name of:
name = "Queen of the South v Dundee - Match Betting"
without the need of going through each individual dictionary and finding its object for key?
You can use valueForKeyPath for that. It accepts a path, separated by dots. Example:
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://dl.dropboxusercontent.com/u/1365846/21680479.json"]]
options:0
error:nil];
NSLog(#"%#", [dict valueForKeyPath:#"response.williamhill.class.type.market.name"]);
This depends on the representation of dictionary. If the williamhill part is changing, then it does not work, of course.
There is no way to obtain a reference within a map data type (in this case, an NSDictionary) without traversing it. Think of the simplified version of this problem: You have a linked list with N elements and you wish to reach the N'th element. Because this is a linked list, you'll have to go through all other N-1 nodes in order to obtain the last reference.
An NSDictionary is a hash based data type in which keys and values are stored. In the case you describe, you have no reference to the nested object (an NSDictionary itself) so you must also traverse all of the dictionaries containing it.
Hope this helps point you in the right direction.
Related
I have recently used the following code to extract the ID of a location from a Foursquare API call with:
NSDictionary* foursquareJson = [NSJSONSerialization JSONObjectWithData:secureData options:kNilOptions error:&error];
NSDictionary *venuesDict = foursquareJson[#"response"];
NSArray *venuesArray = venuesDict[#"venues"];
NSDictionary *venuesDict2 = venuesArray[0];
NSArray *categoriesDict = venuesDict2[#"categories"];
NSDictionary *idDict = categoriesDict[0];
NSLog(#"ID is %#",idDict[#"id"]);
with original foursquareJson being:
2015-03-30 17:16:40.700 Voyagic[2833:718563] {
meta = {
code = 200;
};
response = {
venues = (
{
categories = (
{
icon = {
prefix = "https://ss3.4sqi.net/img/categories_v2/building/conventioncenter_";
suffix = ".png";
};
id = 4bf58dd8d48988d1ff931735;
name = "Convention Center";
pluralName = "Convention Centers";
primary = 1;
shortName = "Convention Center";
}
);
contact = {
formattedPhone = "+44 20 7222 5000";
phone = "+442072225000";
};
hereNow = {
count = 0;
groups = (
);
summary = "Nobody here";
};
id = 4b6599d4f964a520f8f52ae3;
location = {
address = "Broad Sanctuary";
cc = GB;
city = London;
country = "United Kingdom";
distance = 2167;
formattedAddress = (
"Broad Sanctuary",
London,
"Greater London",
"SW1P 3EE",
"United Kingdom"
);
lat = "51.49997800145596";
lng = "-0.1289014132864838";
postalCode = "SW1P 3EE";
state = "Greater London";
};
name = "Queen Elizabeth II Conference Centre";
referralId = "v-1427732200";
specials = {
count = 0;
items = (
);
};
stats = {
checkinsCount = 3657;
tipCount = 15;
usersCount = 2407;
};
verified = 0;
}
);
};
}
but there surely must be a better way of accessing the ID which I don't know about (instead of creating 4 dictionaries and 2 array, which seems somewhat excessive :/ ). Any help would be greatly appreciated :)
Ultimately, the data will need to be accessed through the lists of dictionaries and arrays somehow, it just depends on where you want that to happen. You could use or make a parser for the JSON but that will ultimately still need to map the JSON data similarly to what you are doing. A simple and shorter way of accessing the data would be to not create a new variable in every iteration. Although it really is not much different:
NSDictionary *foursquareJson = [NSJSONSerialization JSONObjectWithData:secureData options:kNilOptions error:&error];
NSDictionary *objectId = foursquareJson[#"response"][#"venues"][0][#"categories"][0][#"id"];
NSLog(#"ID is %#",objectId);
Because of the dialogue in the comments of this answer I figured I should probably include a bit more information about your concern with creating "4 dictionaries and 2 arrays". When you use the JSON Serializer to create native objects from the JSON (first line above) you are creating all of the arrays and dictionaries needed to fully represent and store the entire JSON. The difference in code samples between what you originally posted and what I provided is really not a significantly different. If you are concerned with creating too many dictionaries or arrays you should attempt to filter out the JSON prior to deserializing it into native objects.
I have this NSDictionary that i received from an api call:
{
items = (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
embeddable = 1;
epub = {
isAvailable = 0;
};
pdf = {
isAvailable = 0;
};
publicDomain = 0;
quoteSharingAllowed = 0;
textToSpeechPermission = ALLOWED;
viewability = PARTIAL;
webReaderLink = "http://books.google.com/books/reader?id=LdB2_WzYpKgC&hl=&printsec=frontcover&output=reader&source=gbs_api";
};
etag = 1xxAlevFUSc;
id = "LdB2_WzYpKgC";
kind = "books#volume";
saleInfo = {
country = US;
isEbook = 0;
saleability = "NOT_FOR_SALE";
};
searchInfo = {
textSnippet = "The saga of their daily exploits won cartoonist Bill Watterson the coveted Reuben Award for "Outstanding Cartoonist of the Year." Something Under the Bed Is Drooling is a jewel.";
};
selfLink = "https://www.googleapis.com/books/v1/volumes/LdB2_WzYpKgC";
volumeInfo = {
authors = (
"Bill Watterson"
);
averageRating = "4.5";
canonicalVolumeLink = "http://books.google.com/books/about/Something_Under_the_Bed_Is_Drooling.html?hl=&id=LdB2_WzYpKgC";
categories = (
Humor
);
contentVersion = "0.0.1.0.preview.1";
description = "\"Be good to yourself: Buy a copy of this Calvin and Hobbes cartoon book. If you don't laugh out loud at every third strip, check your pulse. You may be dead.\" --Phil Musick, Pittsburgh Press Calvin is a rambunctious six-year-old whose manic antics threaten world peace. Hobbes is his stuffed tiger who comes alive when adults aren\"t around. The saga of their daily exploits won cartoonist Bill Watterson the coveted Reuben Award for \"Outstanding Cartoonist of the Year.\" Something Under the Bed Is Drooling is a jewel.";
imageLinks = {
smallThumbnail = "http://bks8.books.google.com/books?id=LdB2_WzYpKgC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api";
thumbnail = "http://bks8.books.google.com/books?id=LdB2_WzYpKgC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api";
};
industryIdentifiers = (
{
identifier = 0836218256;
type = "ISBN_10";
},
{
identifier = 9780836218251;
type = "ISBN_13";
}
);
infoLink = "http://books.google.com/books?id=LdB2_WzYpKgC&dq=isbn:0836218256&hl=&source=gbs_api";
language = en;
pageCount = 127;
previewLink = "http://books.google.com/books?id=LdB2_WzYpKgC&printsec=frontcover&dq=isbn:0836218256&hl=&cd=1&source=gbs_api";
printType = BOOK;
publishedDate = "1988-01-01";
publisher = "Andrews McMeel Publishing";
ratingsCount = 18;
readingModes = {
image = 1;
text = 0;
};
title = "Something Under the Bed Is Drooling";
};
}
);
kind = "books#volumes";
totalItems = 1;
}
What I want to do is create an object from the items in this dictionary. This method needs to work generically for all books without crashing. Even if they are missing some of the attributes. The attributes I need to extract are:
-authors last name (Need for every author listed if there is more than one)
-authors first name (Need for every author listed if there is more than one)
-title
-description
-smallThumbnail
-thumbnail
-isbn_10 identifier
-category
From these I am going to create a "book" object. i will also create an NSSet of Authors, and each Author has a lastName and firstName
How do I go about doing this?
You should be able to safely use NSDictionary's -valueForKeyPath: to retrieve each value from an item dict:
NSArray *names = [itemDict valueForKeyPath:#"volumeInfo.authors"];
NSString *title = [itemDict valueForKeyPath:#"volumeInfo.title"];
...
I'm requesting a SOAP Webservice and I parse the response to a NSDictionary. The XML response has multiple unique keys. Here's an example (already parse on the dictionary):
"typ:partits" = {
"typ:dadesPartit" = {
"typ:aforament" = 1;
"typ:codiEsdeveniment" = 2;
"typ:competicio" = GAMPER;
"typ:dataHoraConfirmada" = false;
"typ:dataPartit" = "08/18/14";
"typ:descripcioPartit" = "FCBARCELONA - CLUB LEON F.C.";
"typ:horaPartit" = "9:30:00 PM";
"typ:jornada" = 99;
"typ:partitActiuMenor" = true;
"typ:temporada" = "2014-2015";
"typ:tipusEsdeveniment" = 0;
},
"typ:dadesPartit" = {
"typ:aforament" = 1;
"typ:codiEsdeveniment" = 2;
"typ:competicio" = GAMPER;
"typ:dataHoraConfirmada" = false;
"typ:dataPartit" = "08/26/14";
"typ:descripcioPartit" = "FCBARCELONA - REAL MADRID";
"typ:horaPartit" = "9:30:00 PM";
"typ:jornada" = 101;
"typ:partitActiuMenor" = true;
"typ:temporada" = "2014-2015";
"typ:tipusEsdeveniment" = 0;
};
};
How can I iterate through these keys?, they are the same :( ...
I tried with "allObjects" but when I receive only one "typ:dadesPartit" object it treats it like an array instead of a NSDictionary.
it is returning one type as both your keys are same,
Try renaming the keys to separate names, and the values associated with those keys will come when u type allKeys
In My Application i am Using ArcGIS . in that one i want Search one place and get the places related to that name. Now I want to Parse all the address of that places and put them in a table View. after typing the address and after i press search Button i will get all the Markers on the Map. Now i'm trying to get the complete address of the Place But i was unable to do this.I want to get "place_addr" value from the below string can any one please help me how can i get that value in ios
AGSFindLocationResult name=Disneyland, extent=AGSEnvelope: xmin = -13129988.221853, ymin = 4000701.846518, xmax = -13124422.247314, ymax = 4007401.106877, spatial reference: [AGSSpatialReference: wkid = 102100, wkt = null], graphic=geometry: AGSPoint: x = -13127204.667522, y = 4004050.640580, spatial reference: [AGSSpatialReference: wkid = 102100, wkt = null], symbol: { AGSPictureMarkerSymbol: imageName: BluePushpin.png, width: 36.000000, height: 36.000000 }, attributes: {
AddBldg = "";
AddNum = "";
AddNumFrom = "";
AddNumTo = "";
"Addr_type" = POI;
City = Anaheim;
Country = USA;
DisplayX = "-117.923687";
DisplayY = "33.815467";
Distance = 0;
LangCode = "";
"Loc_name" = "Gaz.WorldGazetteer.POI2";
"Match_addr" = Disneyland;
Nbrhd = "";
Phone = "(714)781-4565";
PlaceName = Disneyland;
"Place_addr" = "1313 S Disneyland Dr Anaheim, CA 92802";
Postal = "";
PostalExt = "";
Rank = "2.5";
Region = California;
Score = 100;
Side = "";
StAddr = "";
StDir = "";
StName = "";
StPreDir = "";
StPreType = "";
StType = "";
Subregion = Orange;
Type = "Amusement Park";
URL = "";
X = "-117.923687";
Xmax = "-117.898691";
Xmin = "-117.948691";
Y = "33.815467";
Ymax = "33.84047000000002";
Ymin = "33.79047";
}, visible: 1
IF what you're getting back really is a string, then you should look at the string searching methods like rangeOfString.
That will return an NSRange that contains your label, "place_addr".
Then you'd want to search from that point forward for a semicolon, using the related method rangeOfString:options:range:, that searches in a specific range of a string.
Finally, you'd need to calculate a range for the contents, that would be 5 characters past the end of the NSRange of "place_addr", and up to, but not including, the final semicolon.
Then you'd use substringWithRange to extract the string who's range you calculated above.
Give that a try, and come back if you have trouble.
Please try the code below
- (NSArray *)findKey:(NSString *)key inString:(NSString *)allString
{
NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:1];
NSArray *splitArray = [allString componentsSeparatedByString:#";"];
for (NSString *s in splitArray) {
NSArray * keyValueArry = [s componentsSeparatedByString:#"="];
if([keyValueArry count] > 1)
{
NSString *_key = [keyValueArry objectAtIndex:0];
NSString *_value = [keyValueArry objectAtIndex:1];
if([_key isEqualToString:key])
{
[result addObject:_value];
}
}
}
return result;
}
where key is place_addr, and allString is as your sample string.
I receive and array of dictionaries from a JSON feed and assign it to an NSMutableArray called jsonArray i.e:
jsonArray = [deserializedData objectForKey:#"reports"];
The feed looks like this:
reports = (
{
address = "The street";
email = "alex#blah.co.uk";
"eng_id" = 1;
"eng_name" = "Alex McPherson";
"eng_thumb" = "http://someurl/image/1.png";
form = Test;
id = 59;
lat = "51.1438330";
live = 1;
lng = "0.8693330";
location = "17 Victoria Crescent, Ashford, TN23 7HL";
name = "Alex McPherson";
phone = 01233000000;
rid = "A5C963-C95B-C3D639";
title = "#A5C963-C95B-C3D639, Litter";
tm = "2013-04-28 20:44:20";
type = 5;
"type-text" = "Litter";
},
{
address = "The street";
email = "alex#blah.co.uk";
"eng_id" = 2;
"eng_name" = "Rob Burt";
"eng_thumb" = "http://someurl/image/1.png";
form = Test;
id = 122;
lat = "51.1415000";
live = 1;
lng = "0.8715000";
location = "38 Beaver Road, Ashford, TN23 7RP";
name = Alex;
phone = 01233000000;
rid = "5A5C96-9072-6BAFA9";
title = "#5A5C96-9072-6BAFA9, Litter";
tm = "2013-04-28 20:35:56";
type = 8;
"type-text" = "Litter";
};
what I would like to do is insert a new value for key lets say into this jsonArray: distance = "0.16km" but my brain is just not working tonight....
so the new jsonArray should looks like this with the added key value mentioned above:
reports = (
{
address = "The street";
email = "alex#blah.co.uk";
"eng_id" = 1;
"eng_name" = "Alex McPherson";
"eng_thumb" = "http://someurl/image/1.png";
form = Test;
id = 59;
lat = "51.1438330";
live = 1;
lng = "0.8693330";
distance = "0.16km";
location = "17 Victoria Crescent, Ashford, TN23 7HL";
name = "Alex McPherson";
phone = 01233000000;
rid = "A5C963-C95B-C3D639";
title = "#A5C963-C95B-C3D639, Litter";
tm = "2013-04-28 20:44:20";
type = 5;
"type-text" = "Litter";
},
{
address = "The street";
email = "alex#blah.co.uk";
"eng_id" = 2;
"eng_name" = "Rob Burt";
"eng_thumb" = "http://someurl/image/1.png";
form = Test;
id = 122;
lat = "51.1415000";
live = 1;
lng = "0.8715000";
distance = "2.13km";
location = "38 Beaver Road, Ashford, TN23 7RP";
name = Alex;
phone = 01233000000;
rid = "5A5C96-9072-6BAFA9";
title = "#5A5C96-9072-6BAFA9, Litter";
tm = "2013-04-28 20:35:56";
type = 8;
"type-text" = "Litter";
};
basically I have a calculation that takes the long and lat from the feed and works out how far the poi is from my current location of which then I sort the array by using a sort descriptor based on the distance key that I want to insert above. I have the code for this just stuck on inserting into the existing nsmutablearray above
Read the deserialized JSON array as an NSArray (not NSMutableArray).
Then create a mutable copy of that array using something like:
NSMutableArray *mutableArray = [originalArray mutableCopy];
Then insert items into mutableArray.