I'm trying to parse a JSON page in Objective-C by creating a subclass of NSDictionary and adding getSomeProperty methods. I have been able to do this with JSON pages that precede every [ or { with keys but am having trouble parsing the following sort of page
[ {"id":12345,"name":"name1","account_id":10002000015631,
"start_at":"2015-09-02T20:24:13","enrollments":
[{"type":"student","role":"enrollment","role_id":821,
"user_id":10000001736511,"enrollment_state":"active"}],"hide_final_grades":false,
"workflow_state":"available","restrict_enrollments_to_course_dates":false},
{"id":100000055661076,"name":"name2","account_id":100000230095635,
"start_at":"2015-08-28T21:22:41Z","grading_standard_id":null,"is_public":null,
"course_code":"name2","default_view":"wiki","enrollment_term_id":10003000007529,"end_at":null,
"public_syllabus":false,"storage_quota_mb":500,"is_public_to_auth_users":false,
"apply_assignment_group_weights":false,"calendar":{"ics":"https://someurl.ics"},
"enrollments":[{"type":"student","role":"StudentEnrollment","role_id":821,
"user_id":10000001736511,"enrollment_state":"active"}],"hide_final_grades":false,"
workflow_state":"available","restrict_enrollments_to_course_dates":false}
]
For example, for this webpage http://www.raywenderlich.com/demos/weather_sample/weather.php?format=json
I am able to create methods
- (NSDictionary *)currentCondition
{
NSDictionary *dict = self[#"data"];
NSArray *ar = dict[#"current_condition"];
return ar[0];
}
and
-(NSString*) cloudcover
{
return self[#"cloudcover"];
}
to retrieve the string #"16".
How can I use a similar method to get the #"name1" or the id #"12345" from my first example JSON code?
You have an array of dictionaries. To have an array of outputs you can use the following method:
- (NSMutableArray *)getValueString: (NSString*)string fromArray: (NSArray *)inputArray {
NSString *outputString;
NSMutableArray *outputArray = [NSMutableArray new];
for (id dict in inputArray) {
if ([[dict class] isSubclassOfClass:[NSDictionary class]]) {
outputString = [dict valueForKey: string];
}
else {
outputString = #"Name not found";
}
if (!(outputString.length > 0)) {
outputString = #"Name not found";
}
[outputArray addObject: outputString];
}
return outputArray;
}
And use it to get name with:
NSArray *resultArray = [self getValueString: #"name" fromArray: inputArray];
NSString *firstName = resultArray[0];
And to get id with:
NSArray *resultArray = [self getValueString: #"id" fromArray: inputArray];
NSString *firstId = resultArray[0];
The [ ] at the beginning and end of your JSON string indicate that it is an array, so when you parse the JSON, you will get an NSArray*, not an NSDictionary*. The first element of the array is an object, so that will be an NSDictionary*. Access id like this:
NSNumber* id = self[0][#"id"];
It looks like your currentCondition is a category on NSDictionary. If that's true and you want the above code to work, you need to make it on a category of NSArray. If it's not true, I don't understand what self is without more info.
Related
I have the following object C code for receiving PubNub message.
- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
NSLog(#"Received message: %# on channel %# at %#", message.data.message,
message.data.subscribedChannel, message.data.timetoken);
}
The returned data is
Received message: (
{
key = userName;
value = Enoch;
},
{
key = photoID;
value = 3;
},
{
key = userID;
value = 1;
},
{
key = actionType;
value = chat;
},
{
key = message;
value = H;
}
) on channel chat at 14888810882049989
I would like to parse the message to a dictionary object for accessing the "value" by using the "key"
I am very new in objective C programming and don't know how to do.
Please help.
Loop through the message array and set the key value in dictionary.
NSArray *array = (NSArray*)message.data.message;
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
for (NSDictionary *item in array) {
[dic setObject:[item objectForKey:#"value"] forKey:[item objectForKey:#"key"]];
}
NSLog(#"%#", dic);
Or
NSArray *array = (NSArray*)message.data.message;
NSArray *values = [array valueForKey: #"value"];
NSArray *keys = [array valueForKey: #"key"];
NSDictionary *dic = [[NSDictionary alloc] initWithObjects:values forKeys:keys];
NSLog(#"%#", dic);
You can use below method for parsing your data and convert it into dictionary
ChatterBoxMessage *chatterBoxMessage = [[ChatterBoxMessage alloc] initFromDictionary: message.data.message withTimeToken: message.data.timetoken];
[chatterBoxMessage asDictionary];
By This method you will get dictionary.
Above ChatterBoxMessage is a PubNub library class.
Also you can parse your data like below :
for (NSDictionary *objectData in message.data.message) {
NSLog(#"Value : %#",objectData[#"value"]);
NSLog(#"Key : %#",objectData[#"key"]);
}
NSDictionary *myDict = #{#"one":#"1",#"two":#"2"};
for (NSDictionary* tmp in myDict) {
NSLog(#"%#",tmp);
}
resut:
my tmpis NSString
I want to get a dictionary with key= one , value = 1
for in for NSDictionary will iterate the keys.
for (NSString * key in myDict) {
NSLog(#"%#",key);
NSString * value = [myDict objectForKey:key];
}
If you want to get a dictionary. You have to create a dictionary from these values
for (NSString * key in myDict) {
NSLog(#"%#",key);
NSString * value = [myDict objectForKey:key];
NSDictionary * dict = #{key:value};
}
Or you should init like this:
NSArray *arrDict = #[{#{"one":#"1"},#{#"two":#"2"}];
for (NSDictionary* tmp in arrDict) {
NSLog(#"%#",tmp);
}
You can get all keys from your dic then add the key and value to your new dic like this:
NSDictionary *myDict = #{#"one":#"1",#"two":#"2"};
NSArray *keys = [myDict allKeys];
for (NSString *key in keys) {
NSDictionary *yourDic = #{key: [myDict valueForKey:key]};
NSLog(#"%#", yourDic);
}
You didn't create it that way. If you wanted to have a NSDictionary inside another NSDictionary you should write something like this :
NSDictionary *myDict = #{
#"firstDict" : #{
#"one":#"1"
},
#"secondDict": #{
#"two":#"2"
}
};
Above code will create a NSDictionary with two dictionaries at keys #firstDict and #secondDict.
Also, bear in mind, that because dictionaries are key-value pairs, using a for-in loop, actually loops through the keys in that dictionary. So your code is equivalent to:
for(NSString *key in dict.allKeys) { ... }
I got the solution
NSDictionary *myDict = #{#"one":#"1",#"two":#"2"};
NSMutableArray *arrayObject = [[NSMutableArray alloc]init];
NSMutableArray *arrayKey = [[NSMutableArray alloc]init];
NSMutableArray *arrayObjectKey = [[NSMutableArray alloc]init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
for (NSString *stringValue in myDict.allValues)
{
[arrayObject addObject:stringValue];
}
for (NSString *stringKey in myDict.allKeys)
{
[arrayKey addObject:stringKey];
}
for(int i = 0;i<[arrayKey count];i++)
{
dict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:[NSString stringWithFormat:#"%#",[arrayKey objectAtIndex:i]],#"key",nil];
[dict setObject:[NSString stringWithFormat:#"%#",[arrayObject objectAtIndex:i]] forKey:#"value"];
[arrayObjectKey addObject:dict];
}
NSLog(#"The arrayObjectKey is - %#",arrayObjectKey);
The Output is
The arrayObjectKey is -
(
{
key = one;
value = 1;
},
{
key = two;
value = 2;
}
)
Create the dictionary:
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:#"1",#"One",#"2","Two",nil];
Get a value out using:(this example tmp will be 1)
NSString *tmp = [myDict objectForKey:#"One"];
Display the output in console:
NSLog(#"%#",tmp);
To display the whole NSDictionary
NSLog (#"contents of myDict: %#",myDict);
What you are doing is creating a dictionary with key-value pairs. I think what you want to do is have an array with dictionaries.
NSArray *myArray = #[#{#"one":#"1"}, #{#"two":#"2"}];
for (NSDictionary* tmp in myArray) {
NSLog(#"%#",tmp);
}
However I don't see a point in doing this. What you could do is:
NSDictionary *myDict = #{#"one":#"1",#"two":#"2"};
for (NSString* key in [myDict allKeys]) {
NSLog(#"%# = %#", key, myDict[key]);
}
I am using webservice to pull out some information in json format. I am able to view them in my app. However when I try to store them in dictionary i keep getting error saying value "courseId" not compliant. This is what I have
NSMutableArray *results = [NSMutableArray array];
for (id courseDictionary in response) {
ListOfCourses *course = [[ListOfCourses alloc] initWithDictionary:courseDictionary];
[results addObject:course];
}
and in my ListOfCourses.m I have
- (id)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
_courseId = [NSNumber numberWithInt:[[dictionary valueForKey:#"courseId"]
_room1 = [dictionary objectForKey:#"room1"];
_subjectCode = [dictionary objectForKey:#"subjectCode"];
}
return self;
}
json file
{
"Template":{
"Room1":"Day 1 room name/number; string; required; 10 characters",
"SubjectId":"Subject identifier; number; required; Subject identifier must exist",
{
"Links":[
{
"Rel":"self",
"Href":"/api/courses/2"
},
{
"Rel":"collection",
"Href":"/api/courses/"
}
],
"Item":{
"Link":{
"Rel":"self",
"Href":"/api/courses/2"
},
"Id":1,
"SubjectCode":"D9920",
"Room1":"S2153"
}
UPDATED: I am returning a collection of objects, when i do this
_courses = (NSArray *)[response objectForKey:#"Collection"];
I am able to view the objects. I am trying to put them each individually into dictionary now
When you are taking entities on and off a stream, you should use the NSCoding protocol. There are many ways to do that with JSON, e.g. JSONCoding.
These types of problems would qualify as refactor smells: you should not have to worry about how something was bundled up.
Change your line of code from:
_courseId = [NSNumber numberWithInt:[[dictionary valueForKey:#"courseId"]
to:
_courseId = [NSNumber numberWithInt[[dictionary valueForKey:#"Id"] intValue]];
If this won't help please post the json file.
//EDITED
You use [dictionary valueForKey:#"courseId"] but the key is Id, please change courseId to Id.
//EXTENDED
You have dictionary embedded in dictionary try this:
NSDictionary *dic = dictionary[#"Item"];
_courseId = [NSNumber numberWithInt:[dic[#"Id"] intValue]];
_room1 = dict[#"Room1"];
_subjectCode = dict[#"SubjectCode"];
If it doesn't work you have to post beginning of your json file.
I want to give a key value from my NSDictionary and get the value associated to it.
I have this:
NSArray *plistContent = [NSArray arrayWithContentsOfURL:file];
NSLog(#"array::%#", plistContent);
dict = [plistContent objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"code"];
with plistContent :
(
{
code = world;
key = hello;
},
{
code = 456;
key = 123;
},
{
code = 1;
key = yes;
}
)
So how do I get "hello" by giving the dictionary "world"?
If I understand your question correctly, you want to locate the dictionary where "code" = "world" in order to get the value for "key".
If you want to keep the data structure as it is, then you will have to perform a sequential search, and one way to do that is simply:
NSString *keyValue = nil;
NSString *searchCode = #"world";
for (NSDictionary *dict in plistContents) {
if ([[dict objectForKey:#"code"] isEqualToString:searchCode]) {
keyValue = [dict objectForKey:#"key"]); // found it!
break;
}
}
However if you do alot of this searching then you are better off re-organizing the data structure so that it's a dictionary of dictionaries, keyed on the "code" value, converting it like this:
NSMutableDictionary *dictOfDicts = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in plistContents) {
[dictOfDicts setObject:dict
forKey:[dict objectForKey:#"code"]];
}
(note that code will break if one of the dictionaries doesn't contain the "code" entry).
And then look-up is as simple as:
NSDictionary *dict = [dictOfDicts objectForKey:#"world"]);
This will be "dead quick".
- (NSString*) findValueByCode:(NSString*) code inArray:(NSArray*) arr
{
for(NSDictonary* dict in arr)
{
if([[dict valueForKey:#"code"] isEqualToString:code])
{
return [dict valueForKey:#"key"]
}
}
return nil;
}
I have the following dictionary set up(Object, Key)
0, "10;0.75,0.75"
1, "0;2.25,2.25"
3, "1;3.5,2.0"
4, "1;4.5,3.0"
5, "2;6.0,5,0"
What I want to filter will be based on the object AND the key. The object is a NSNumber. The key is a string but i really don't want the entire string. I want to split the string separated by the semicolon and take the first index of the split which would yield the strings 10,0,1,1 or 2 depending on which object I was looking for.
As a specific example:
Are there any keys that are equal to #"1" with an object that is greater than 3.
In this case i should expect back YES since object 4 has a key that is equal to #"1", after i do the split.
I guess I was looking for a clever way to define a NSPredicate to do the split on the key separated by the semicolon and then filter(compare, etc) based on that. Let me know if you have any questions or need additional info.
A very naive implementation that I could think of
- (BOOL)hasKey:(NSString *)key withValueGreaterThan:(id)object{
NSDictionary *dictionary = #{#"10;0.75,0.75": #0,
#"0;2.25,2.25" : #1,
#"1;3.5,2.0" : #3,
#"1;4.5,3.0" : #4,
#"2;6.0,5,0" : #5};
NSPredicate *keyPredicate = [NSPredicate predicateWithFormat:#"SELF BEGINSWITH %#",key];
NSArray *filteredKeys = [[dictionary allKeys]filteredArrayUsingPredicate:keyPredicate];
for (NSString *k in filteredKeys) {
NSNumber *value = dictionary[k];
if (value>object) {
return YES;
}
}
return NO;
}
Use
BOOL hasValue = [self hasKey:#"1;" withValueGreaterThan:#3];
Sample Code:
NSDictionary* dict = #{ #"10;0.75,0.75":#0,
#"0;2.25,2.25":#1,
#"1;3.5,2.0":#3,
#"1;4.5,3.0":#4,
#"2;6.0,5,0":#5};
__block NSString* foundKey = nil;
[dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSNumber* obj, BOOL *stop) {
//here goes condition
//get substr
NSArray* arr = [key componentsSeparatedByString:#";"];
int num = [[arr objectAtIndex:0]integerValue];
if ((num == 1)&&([obj integerValue]>3)) {
foundKey = key;
stop = YES;
}
}];
if (foundKey) {
NSLog(#"%#:%#",foundKey,[dict objectForKey:foundKey]);
}
Just use the following method:
-(BOOL)filterFromDictionary:(NSDictionary*)dict keyEqual:(NSString*)key greaterthanObj:(NSString*)obj
{
NSArray *allKeys = [dict allKeys];
for (NSString *eachkey in allKeys) {
NSString *trimmedKey = [self trimKeyuntill:#";" fromString:eachkey];
NSString *trimmedValue = [dict objectForKey:eachkey];
if ([trimmedKey isEqualToString:key] && [trimmedValue intValue] > [obj intValue]) {
return YES;
}
}
return NO;
}
call the above method with your dictionary like:
NSDictionary *dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"1",#"1",#"3",#"4",#"5", nil] forKeys:[NSArray arrayWithObjects:#"10;0.75,0.75",#"0;2.25,2.25",#"1;3.5,2.0",#"1;4.5,3.0",#"2;6.0,5,0", nil]];
[self filterFromDictionary:dict keyEqual:#"1" greaterthanObj:#"3"]
I assumed all your objects are nsstrings. otherwise change the intValue