I have JSON data coming in using AFNetworking.
The responseObject holds an array of objects like so: [{"id":"XX", "description":"XX"}, {"id":"XX", "description":"XX"}]. This content is copied across to an NSArray, where by value access is obtained with objectAtIndex: valueForKey:.
I know object-c is overly complicated so I'm guessing that this is wishful thinking, but how would I go about creating a quick object to use in the event that responseObject is nil?
(any committed object-c coders, you'll have to excuse my bluntness. working with higher level languages causes logical ignorance)
if responseObject is nil
destinationArray= [{id:XX, description:XX}, {id:XX, description:XX}]
else
destinationArray = responseObject
You can do that with Objective-C literals:
if (!responseObject) {
destinationArray= #[
#{ #"id":#"XX",
#"description":#"XX"},
#{ #"id":#"XX",
#"description":#"XX"}
];
} else {
destinationArray = responseObject;
}
Where:
NSArray objects are #[ ... ].
NSDictionary objects are #{ ... }.
NSString objects are #"string".
NSNumber objects are #(1), #(1.2f), #(1.2), #(YES).
The data you are showing has 2 parts:
Multiple NSDictionaries, there are many ways to create them:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:...,nil];
Or you can define them by using its literal
NSDictionary *dictionary = #{#"id":#"XX", #"description":#"XX"};
An NSArray, to create an array you can use several options:
NSArray *array = [NSArray arrayWithObjects:...,nil];
Or, like before you can use the literal definition:
NSArray *array = #[dictionary1,dictionary2];
So, you can create the entire object like this:
if (responseObject == nil) {
destinationArray = #[#{#"id":#"XX", #"description":#"XX"},
#{#"id":#"XX", #"description":#"XX"}];
} else {
destinationArray = responseObject;
}
Read more at: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/FoundationTypesandCollections/FoundationTypesandCollections.html
Related
I have an NSDictionary created by the AFNetworking class, but now I want to convert that NSDictionary into an array of objects of a class that I made. I have tried using the for-in loop as follows:
for (NSDictionary *item in results)
[_masterList addObject:[[MyObject alloc]initWithType:[item objectForKey:#"type"];
results by the way is my NSDictionary. There are no syntax errors, but when I run my program, nothing comes up. I tried NSLog-ing in the for-in, but didn't get anything, which means that the loop does not even run once. I'm pretty sure the NSDictionary is full, because I NSLoged the result during the AFNetworking process, and it was full:
(
{
address = "Sample Address";
}
)
Anyone have any ideas that I could use? Please express in the comments if I should add any more code. Thanks!
There are no key type, change it to address
for (NSDictionary *item in results)
[_masterList addObject:[[MyObject alloc]initWithType:[item objectForKey:#"address"];
results is a NSDictionary or a NSArray of dictionaries? If it is NSDictionary then you must loop like this:
// loop through each key in dictionary
// let say your key type is NSString
for (NSString* key in results) {
// access your object for key here
NSLog(#"%#", results[key]);
}
Not much of an answer but too big for a comment: you've made a mistake other than in the code presented. Try:
NSArray *results = #[
#{
#"address": #"Sample Address"
}
];
NSLog(#"%#", results);
for (NSDictionary *item in results)
NSLog(#"%# (%#)", item, [item objectForKey:#"address"]);
Output is:
2015-11-10 23:20:22.713 Untitled[1564:1904151] (
{
address = "Sample Address";
}
)
2015-11-10 23:20:22.715 Untitled[1564:1904151] {
address = "Sample Address";
} (Sample Address)
So, definitely the same dictionary, then the same iteration code, with correct results.
Addendum, which may or may not be helpful: one of the things Apple supplies to take advantage of Objective-C's reflective runtime is key-value coding which, assuming your properties obey certain rules, allows them to be written to by name. If you had a dictionary you wanted to commute to an object, and had performed suitable validation, then you might:
NSDictionary *inputDictionary = ... whatever ...
SomeObject *target = [[SomeObject alloc] init];
for (NSString *key in inputDictionary)
[target setValue:inputDictionary[key] forKey:key];
In contrast to Objective-C norms but consistently with most modern languages, that'll raise an exception if the key doesn't exist as a property on target. So in production code you probably want at least a try/catch block if the dictionary is coming from afar.
i'm trying to achieve the following structure:
NSMutableDictionary *dict = [#{} mutableCopy];
NSDictionary *key1 = #{#"id_format": #(1), #"date": #"2014-08-01"};
NSDictionary *key2 = #{#"id_format": #(2), #"date": #"2014-08-02"};
// This runs perfect and can be checked in llvm debugger
// data1 & data2 are NSArray that contain several NSDictionary
[dict setObject:data1 forKey:key1];
[dict setObject:data2 forKey:key2];
// Later, if i try to access dict using another key, returns empty NSArray
NSDictionary *testKey = #{#"id_format": #(1), #"date": #"2014-08-01"}; // Note it's equal to "key1"
for(NSDictionary *dictData in dict[testKey]){
// dictData is empty NSArray
}
// OR
for(NSDictionary *dictData in [dict objectForKey:testKey]){
// dictData is empty NSArray
}
So the question is if is there possible to use NSDictionary as key, or not.
An object can be used as a key if it conforms to NSCopying, and should implement hash and isEqual: to compare by value rather than by identity.
Dictionaries follow the array convention of returning [self count] for hash. So it's a pretty bad hash but it's technically valid. It means your outer dictionary will end up doing what is effectively a linear search but it'll work.
Dictionaries implement and correctly respond to isEqual:. They also implement NSCopying.
Therefore you can use a dictionary as a dictionary key.
As you might expect, i'm fairly new to obj-C, and i'm constantly trying to build knowledge and experience. But i'm still struggling with a lot of concepts, and that includes JSON data 'catching'.
I've seen many tutorials and guides but i just can't translate them into what i need. Most of the time they layout data in arrays or get multiple values, and (of course) use different variables, which makes everything confusing and unclear to me, even though this should be stupidly simple.
I'm trying to do something very simple :
Get a single value from the open weather API, the temperature.
I'll show you my code which, according to my disgraceful knowledge, should be perfect, but apparently it doesn't work :D
#implementation HomeViewController
{
NSMutableArray *tableData;
NSDictionary *jsonDict;
NSMutableString *title;
}
-(void) viewDidLoad
{
[super viewDidLoad];
NSError *error;
//I create my data array and the string i'll store my value later on
tableData = [[NSMutableArray alloc] init];
title = [[NSMutableString alloc]init];
// Creating the link for the json api so it fits coordinates ; this works but i edited the locations out to clear the code
NSString *s = [[NSString alloc]initWithFormat:#"http://api.openweathermap.org/data/2.5/weather?lat=%.05f&lon=%.05f", _annotation.coordinate.latitude, _annotation.coordinate.longitude];
// I go online and catch the data of the url stored in S
NSData *jSonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:s]];
// This is a dictionary where all my data is stored from jsonData, keys and values all the way
jsonDict = [NSJSONSerialization JSONObjectWithData:jSonData options:NSJSONReadingMutableContainers error:&error];
// I use the string created previously and assign it the value stored in that dictionary, in the TEMP 'folder', right under MAIN.
title = [[jsonDict objectForKey:#"main"]objectForKey:#"main.temp"];
// I assign that title to a label so it appears in my view.
self.tempLabel.text = title;
...
}
There you go. I'm probably missing something very simple but i've been stuck on this and even if i feel I know what i'm doing, i'm probably missing something. So it'd be great if with the answer you give me, you could also tell me what I did wrong :D
Thank you very much for your support and knowledge. This community is amazing :)
Put a breakpoint after assigning value to jsonDict and use
po jsonDict
in the console to print out what you are getting. Then, adjust the code that extracts the value. And use modern Objective-C syntax for it.
Example
title = jsonDict[#"main"][#"temp"];
Note
po is a debugger command that will print out the contents of an object. If you need to print out the contents of a primitive, use p instead.
My guess is
jsonDict = [NSJSONSerialization JSONObjectWithData:jSonData options:NSJSONReadingMutableContainers error:&error];
is trying to create an nsdictionary, but it the results are coming back as an array. Try this:
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: jsonDict options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
This should set you right:
title = [[jsonDict objectForKey:#"main"]objectForKey:#"temp"];
To explain the issue, it seems you were referring to temp using a combination of dot syntax in the key.
EDIT: In response to your error:
That error appears when you're trying to find the length of a string on a value that is not of NSString type. Looks like temp is being returned as a number. So, to do what it looks like you're trying to do, you'll wanna convert [[jsonDict objectForKey:#"main"]objectForKey:#"temp"] to an NSString:
NSNumber *temp = [[jsonDict objectForKey:#"main"]objectForKey:#"temp"];
NSString *tempString = [temp stringValue];
OR
NSString *temp = [[[jsonDict objectForKey:#"main"]objectForKey:#"temp"] stringValue];
That will allow you to get the length: temp.length
**EDIT: Unless you're trying to get the length of the array of weather data...in which case i'd like to see more of that code
I've been trying to create a class to allow me to output core data out to JSON.
I have managed to get it working to a point, however I seem to have hit a brick wall on a outputting relationships
NSMutableArray * objectsArray = [[NSMutableArray alloc] init];
for (NSManagedObject * object in array) {
if([NSJSONSerialization isValidJSONObject:object]) {
[objectsArray addObject:object];
} else {
NSMutableDictionary *fields = [NSMutableDictionary dictionary];
for (NSAttributeDescription *attribute in [[object entity] properties]) {
NSString *attributeName = attribute.name;
id attributeValue = [object valueForKey:attributeName];
if([results length] > 0)
{
NSArray *chunks2 = [results componentsSeparatedByString: #","];
for (NSString * string in chunks2) {
if([string.lowercaseString isEqualToString:attributeName.lowercaseString])
{
[fields setObject:[NSString stringWithFormat:#"%#",attributeValue] forKey:attributeName];
break;
}
}
}
else
{
if (attributeValue) {
[fields setObject:[NSString stringWithFormat:#"%#",attributeValue] forKey:attributeName];
}
}
}
[objectsArray addObject:fields];
}
}
NSError *error;
NSData * JSONData = [NSJSONSerialization dataWithJSONObject:objectsArray options:kNilOptions error:&error];
And this outputs data fine aslong as I do not have a relationship for example a one -> many or many -> one
It outputs the following
{
"mySegmentation": "(null)",
"number": "9452062"
},
{
"mySegmentation": "<NSManagedObject: 0x212050b0> (entity: SegmentationCodes; id: 0x212090b0 <x-coredata://BEC52F5F-EA26-4CFF-BCCB-09DA163F465D/SegmentationCodes/p13> ; data: <fault>)",
"number": "9448502"
},
How can I get it to also indent in and output the information from the relationship?
I have been scratching my head for a while on this and would appreciate the help
Thanks Matt
From the documentation:
An object that may be converted to JSON must have the following properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray,
NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
So, what you have to do is compose a dictionary or array with dictionaries, arrays, strings, numbers, nulls.
Normally relationships in CoreData are not sorted, so NSSets, you have to generate a NSArray from the set (therefor a nice method from Apple exists) and put it as value in the dictionary for the specific key.
Then run - dataWithJSONObject:options:error: for example (as you did before) and retrieve the correct JSON.
Not sure if the indention is right. You have to check that out.
Thats it, hopefully
Hope someone could help me with that :
I'm using a NSDictionary to fill a UITableView.
Its model is like [key:userID => value:userName].
The tableView is only filled with userName but when clicked, it has to send the userID related.
The problem comes when I want to filter the UITable. I only found the way to filter a Dictionary by transforming it into NSArray (using Predicate) but it make me loose the relation between userNames and userIDs.
A solution would be to filter the initial NSDictionary to get a filtered NSDictionary (with still the relational key/value), but I don't know how to do that. I only found solutions to get Arrays.
How could I do that, or is there a better solution to do it?
There is a much better solution, François.
Create, from your NSDictionary (I will call it here myDictionary), an NSArray like this (declare it in your interface file):
NSArray *arrayForTableView;
Then, just after you load your NSDictionary, do the following:
arrayForTableView = [myDictionary allKeys]; // so you will have an array of all UserID's
Now, in your tableView: cellForRowAtIndexPath: method, you can do it like this:
cell.textLabel.text = [myDictionary objectForKey:[arraForTableView objectAtIndex:indexPath.row]];
And then, when you will want to pass the userID when the user selects the cell, in your tableView: didSelectRowAtIndexPath: you just do it this way:
id userIDSelected = [arraForTableView objectAtIndex:indexPath.row];
Then, when you want to filter the array according to the search, you can simply recreate your arrayForTableView, by "scanning" your NSDictionary this way:
NSString *typedString;
NSMutableArray *arrayFiltered = [NSMutableArray array];
for (int i = 0; i < [[myDictionary allKeys] count]; i++)
{
if ([[myDictionary objectForKey:[[myDictionary allKeys] objectAtIndex:i]] rangeOfString:typedString].location != NSNotFound)
{
[arrayFiltered addObject:[[myDictionary allKeys] objectAtIndex:i]];
}
}
arrayForTableView = arrayFiltered;
This way, you won't even need to change your UITableView dataSource and delegate methods.
You can do following to get value(userID) for selected key(userName) :
//iterate through whole dictionary
for(id key in yourNSDictionary)
{
// if key is the userName clicked
if([key isEqualToString:selectedUserName])
{
//userID for clicked userName
int userID = [yourNSDictionary objectForKey:#selectedUserName];
}
}
you're using an NSDictionary to populate an UITableView and this UITableView is only filled with the username which you get by doing
[dictionary objectForKey#"userID"];
a NSDictionary has two functions allkeys and allValues
NSArray* allUserID = [dictionary allKeys];
NSArray* allUserNames = [dictionary allValues];
this is a parallel arrays so that the index of one array, runs parallel with it's associated array.
Each cell of the table cell could also be a custom class that holds a reference to it's own id and username, this will allow you to only pass the cell and have it's data.
you can read about those functions in the NSDictionary documentation
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
i would recommend creating an NSArray or NSMutableArray with NSDictionary values - UITableViews are meant to be driven by arrays, where the array index matches the row number. Then you can easily create a custom filter for the array of dictionaries which take into account your data structure. Your code might include parts of this sample code:
NSString *idKey = #"userId";
NSString *nameKey = #"userName";
NSArray *arr = #[
#{
idKey : #(24),
nameKey : #"Oil Can Henry"
},
#{
idKey : #(32),
nameKey : #"Doctor Eggman"
},
#{
idKey : #(523),
nameKey : #"Sparticus"
},
];
NSString *searchTerm = #"Spar";
NSArray *newArray = [arr filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject[nameKey] hasPrefix:searchTerm];
}]];
Advantages:
a single data structure to represent all your data
inherent, deterministic ordering
support for NSPredicate filtering