Objective-C parse json to list of object - ios

I need to parse a json string to a NSMutableArray... I did it as follows:
JsonString = "{
"list":[
{
"IDI":{
"IDI_ID":1
},
"PAR_VPARAM":"param1",
"PAR_VALUE":"value1"
},
{
"IDI":{
"IDI_ID":2
},
"PAR_VPARAM":"param2",
"PAR_VALUE":"value2"
},
{
"IDI":{
"IDI_ID":3
},
"PAR_VPARAM":"param3",
"PAR_VVALUE":"value3"
}
]
}";
NSData *data = [JsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
NSMutableArray *resultArray = [json objectForKeyedSubscript:#"list"];
I have an object called PARAMETERS, and it has the same structure of a single item of JSON: "list". When i parse it, it works, the problem is in the object inside of each item in the json: "IDI", always parse with null value.
for(NSObject *obj in resultArray){
Parameters *paritem = (Parameters *)obj;
int test = paritem.IDI.IDI_ID; //Error here!
}
How can i do it?

NSJSONSerialization won't map your JSON data to your custom class. It will provide NSString, NSNumber, NSDictionary, and NSArray objects (or their mutable counterparts, if you specify the right NSJSONReadingOptions).
If you want to map this data to your Parameters class, you have to provide that logic yourself. You cannot simply cast NSDictionary.
for(NSObject *obj in resultArray){
Parameters *paritem = [[Parameters alloc] init];
paritem.PAR_VPARAM = obj[#"PAR_VPARAM"];
paritem.PAR_VALUE = obj[#"PAR_VALUE"];
// To capture the IDI property, you will either have to
// define a new IDI class with a property named "IDI_ID",
// live with NSDictionary, or add an "IDI_ID" property
// to your Parameters class.
// In this example, I left the value as a dictionary.
paritem.IDI = obj[#"IDI"];
// Here's how you would get the IDI_ID:
NSNumber *IDI_ID = paritem.IDI[#"IDI_ID"];
}
With that out of the way, here are a couple unsolicited stylistic tips:
For variables and properties in Obj-C, lowerCamelCase is conventional. Instead of paritem.PAR_VPARAM, use parItem.parVParam (note the capital I in parItem).
Your class names should have a two- or three-letter "namespace" (much like NSString, UIView, or CGPoint). If you can't come up with a couple letters to represent this specific project, use an abbreviation of your company's name. If all else fails, use your initials.
Your parameter names are extremely vague, and somewhat redundant. Does every property really need to be prefixed with PAR_? Do you really need IDI_ID to be nested within the IDI property of your Parameters object? You could make your code much more readable by being more concise.
Here's what your code might look like if you took this advice (I'm making some assumptions of your source data):
for(NSObject *obj in resultArray){
APParameters *parItem = [[APParameters alloc] init];
parItem.parameterName = obj[#"PAR_VPARAM"];
parItem.parameterValue = obj[#"PAR_VALUE"];
// To capture the IDI property, you will either have to
// define a new IDI class with a property named "IDI_ID",
// live with NSDictionary, or add a property to your
// Parameters class which holds the IDI_ID value directly.
// In this example, I grabbed the IDI_ID value directly.
parItem.itemID = obj[#"IDI"][#"IDI_ID"];
}

First of all, I'd suggest you to cast JSON data to an NSArray instead of NSMutableArray if you're not going to add or remove new objects to the array.
About "IDI" indices, they are not being parsed as strings like other indices since they're dictionaries. You also should create Parameters object manually instead of casting directly.
An example:
// In Parameters.h
#property (nonatomic, strong) NSString *PAR_VPARAM;
#property (nonatomic, strong) NSDictionary *IDI;
Then after parsing JSON,
for (NSDictionary *obj in resultArray){
Parameters *paritem = [[Parameters alloc] init];
paritem.PAR_VPARAM = [obj valueForKey:#"PAR_VPARAM"];
paritem.IDI = [obj valueForKey:#"IDI"];
int test = (int)[paritem.IDI valueForKey:#"IDI_ID"];
}
This should work well and you can create new properties for other JSON indices.

Related

iOS JSON Parsing, Format Issues

So I was working on a project that required me to work with some JSON, I was running into a few issues regarding the best way of representing things. First of, this is how the JSON looks:
"phoneList": [
{
"phoneReason": "End of Contract",
"phoneType": [
{
"id": 5,
"phoneType": "Android Smartphone"
}
]
}
]
I want to know the most appropriate way of representing this.
For example, I do know that that my phoneReason will just be a simple NSString while my phoneType is actually a NSArray. However,I wasn't sure how to represent a)the id, I know this is an integer, but should this be an NSInteger or an NSNumber and b)could someone point me in the direction of some sample code where I can understand how to model a dictionary object containing an integer and a string and also where I can understand how to model an array of dictionaries.
My other question is also similar in that say I'm actually posting something, how do I model this, specifically say for like dictionary type (JSON Curly Brace)objects that contain a number/integer and a string.
For example, this is the JSON I'm trying to model and then do something like this:
"phoneReason": "Upgrade",
"phoneInfo": {
"id": "2"
},
//And then I want to pass ID
-(void) createOurRequest:(NSNumber *)id {
NSDictionary *myDictionary = #{
#"phoneReason" : [NSString stringWithFormat:#"%i", s elf.dat.reason],
//How do I then represent the phoneInfo element exactly?
};
Sorry, for the clumsy question, would really appreciate any guidance on modeling JSON in iOS or just generally.
I'm assuming you're asking questions a) and b), and also how to model a JSON.
a) The unfortunate thing with Obj-C is that all collection elements have to be objects. Integers are value types, so they will need to be converted to NSNumbers to work. However, if you're parsing a JSON string, the builtin JSON parser does it for you. I'll describe it below.
b) The model is based on the JSON. You describe the object collection and the parser will determine the model for you. In your example, you would have a NSDictionary<NSString *: NSArray<NSDictionary<NSString *: id>*>*>. The innermost element has value of id because you can either have an NSString ("End of Contract") or an NSArray ("phoneType": [ { "id": 5, "phoneType": "Android Smartphone" } ])
Of course, the model is defined by your JSON, so if you run it through a parser, you get a structured object. You can access each element based on your model (object[#"phoneList"][#"phoneReason"]).
The class method to use is:
+ (id)JSONObjectWithData:(NSData *)data
options:(NSJSONReadingOptions)opt
error:(NSError **)error
Where you pass it a NSData representation of your string, options (or 0), and a NSError pointer (error*). You get back a parsed JSON with the proper structure you defined.
NSDictionary *parsedJSONObject = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:NULL
I have no options to use and I know there will be no error, so I pass nothing for those parameters. The resulting object will be in whatever structure your JSON is.
Using the objects and the json layout you provided in your first example, this is how I would go about creating the dictionaries and arrays to get the json in the format you specified. Hopefully this helps make it a little clearer for you.
// example constructor method
-(void) jsonStringWithPhoneReason:(NSString*)reason phoneId:(NSInteger)phoneId phoneType:(NSString*)phoneType
{
// create device detail dictionary
NSDictionary *deviceOneDetail = #{
#"id" : #(phoneId), // <- set phone id as NSNumber
#"phoneType" : phoneType // <- set your string phone type
};
// create device dictionary
NSDictionary *deviceOne = #{
#"phoneReason" : reason, // <- set your phone reason string
#"phoneType" : #[deviceOneDetail] // <- set your phone type dictionary within an array
};
// create phone list dictionary with any device dictionaries you want to add
NSDictionary *phoneListDict = #{
#"phoneList" : #[
deviceOne, // <- add your device to the phone list array of dictionaries
// deviceTwo...
]
};
NSString *jsonString = [self convertToJsonString:phoneListDict]; // <- convert the dictionary into a json string and use however you wish
// your json string should now look like this assuming you pass 'End of Contract', 5 & 'Android Smartphone' as arguments to this method
// {"phoneList":[{"phoneReason":"End of Contract","phoneType":[{"id":5,"phoneType":"Android Smartphone"}]}]}
}
-(NSString*) convertToJsonString:(NSDictionary*)dictionary
{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:0 // Pass 0 if you don't care about the readability of the generated string
error:&error];
if (error)
{
NSString *errorDesc = [NSString stringWithFormat:#"Error creating json data from dictionary: %#", error.localizedDescription];
NSLog(#"ERROR: %#", errorDesc);
jsonData = nil;
return nil;
}
NSString *returnString = nil;
if(jsonData != nil)
{
returnString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
return returnString;
}

How can I implement my logic properly to populate my UITableView

Sorry guys, this problem I am running into is pretty trivial. I just can't wrap my head around it so hope someone can help me. Your help is really appreciated. I am getting JSON data through NSURLConnectDelegate with a web API. I get something like this back:
(
{
id = 340
name = Vicent },
{
id = 339
name = Johny },
{
id = 338
name = Eric }
)
and I save it in a NSMutableArray as a global variable. Now, I have a NSSet of "ids". For example:
{
340, 339
}
In the numberOfRowsInSection, I return the set's count. I am trying to load only the ids in the NSSet from the array with the data saved from the webAPI, so I do something like this in cellForRowIndexPath:
for (NSNumber *num in [set allObjects]) {
NSString *newString = [[savedArray objectAtIndex:indexPath.row]
NSString *new = [num stringValue];
if ([new isEqual:newString]) {
}}
How can I just populate the ids I want?
The JSON makes it look like you have an array of dictionaries, which is a reasonable data structure to use as the data source for a table view.
It sounds like you're trying to filter your array to only include the items that are in your set. Is that right?
If so, you could write code that would create a new array containing the subset of your array elements who's ID is also in your set. There are at least a half-dozen ways to do that. One fairly simple approach would be to use the NSArray method indexesOfObjectsPassingTest. You'd pass that method a block of code that would check each array element to see if it's id object was in your set.
That would give you an NSIndexSet with the indexes of the items in your array who's ID are in your set. Then you could use the NSArray method objectsAtIndexes to get an array of only the objects that are also in the set. Something like this (Assuming that your array of dictionaries is called savedArray and your set is called allObjects:
//get the indexes of items in the array savedArray who's id appears in the set allObjects
NSIndexSet *indexes = [savedArray indexesOfObjectsPassingTest:
^(NSDictionary *obj,
NSUInteger idx,
BOOL *stop)
{
return [allObjects member: obj[#"id"]] != nil;
}
];
//Now build an (immutable) array of just the objects who's ID are in the set
NSArray *subArray = [savedArray objectsAtIndexes: indexes];
The array subArray created above is immutable. If you need a mutable array you would need to make a mutable copy, which is a one-line change.
Disclaimer: I still struggle a little with block syntax, so the above might not be exactly correct, but it gives you the general idea.

Want to save all the valueForKey in dictionary in either 1 array or dictionary

for (NSDictionary *fbDictionary in self.latestReactionsArray) {
LatestReaction *latestReaction = [[LatestReaction alloc]init];
NSDictionary *subFBDictionary = [fbDictionary objectForKey:#"participant"];
NSString *facebookUserID = [subFBDictionary valueForKey:#"facebook_id"];
NSNumber* reactionIDNum = [fbDictionary valueForKey:#"reaction_id"];
int reactionID = [reactionIDNum intValue];
NSLog(#"what is name%# and %# and %d",facebookUserID, self.latestReactionsArray,reactionID);
}
I want to save all [fbDictionary valueForKey:#"reaction_id"] in an array or dictionary. How do I do this? Thanks.
Try this:
NSArray *reactionIDs = [self.latestReactionsArray valueForKey:#"reaction_id"];
That will give you an array of reaction IDs.
The reflection in Objective C is not powerful enough to get a usable list of properties that you want to map. Instead, you should implement a class method that returns a list of properties you want to map to JSON and use that.
Lastly, a common "Gotcha" is trying to add nil to a dictionary. You'll need to do a conversion from nil to [NSNull null] and back for the conversion to work properly.

How to parse JSON in iOS App

Im getting a response from twitter in the form of a string,
What I need is to send the parts where is a comment to an array,
here an example of the string
[{"geo":null,"coordinates":null,"retweeted":false,...
"text":"#KristinaKlp saluditos y besos d colores!"},{"geo":null,"coordinates...
so what I really need are the posts after "text":" =
#KristinaKlp saluditos y besos d colores!
So, how can I take the string and parse it so I get all the messages in an array hopefully?
Thanks a lot!
I haven't done JSON parsing myself in an iOS App, but you should be able to use a library like the json-framework. This library will allow you to easily parse JSON and generate json from dictionaries / arrays (that's really all JSON is composed of).
SBJson docs:
JSON is mapped to Objective-C types in the following way:
null -> NSNull
string -> NSString
array -> NSMutableArray
object -> NSMutableDictionary
true -> NSNumber's -numberWithBool:YES
false -> NSNumber's -numberWithBool:NO
integer up to 19 digits -> NSNumber's -numberWithLongLong:
all other numbers -> NSDecimalNumber
Since Objective-C doesn't have a dedicated class for boolean values,
these turns into NSNumber instances. However, since these are
initialised with the -initWithBool: method they round-trip back to JSON
properly. In other words, they won't silently suddenly become 0 or 1;
they'll be represented as 'true' and 'false' again.
As an optimisation integers up to 19 digits in length (the max length
for signed long long integers) turn into NSNumber instances, while
complex ones turn into NSDecimalNumber instances. We can thus avoid any
loss of precision as JSON allows ridiculously large numbers.
#page objc2json Objective-C to JSON
Objective-C types are mapped to JSON types in the following way:
NSNull -> null
NSString -> string
NSArray -> array
NSDictionary -> object
NSNumber's -initWithBool:YES -> true
NSNumber's -initWithBool:NO -> false
NSNumber -> number
#note In JSON the keys of an object must be strings. NSDictionary
keys need not be, but attempting to convert an NSDictionary with
non-string keys into JSON will throw an exception.
NSNumber instances created with the -numberWithBool: method are
converted into the JSON boolean "true" and "false" values, and vice
versa. Any other NSNumber instances are converted to a JSON number the
way you would expect.
Tutorials
Are there any tutorials? Yes! These are all tutorials provided by
third-party people:
JSON Framework for iPhone - a Flickr tutorial in three parts by John
Muchow. JSON Over HTTP On The iPhone - by Dan Grigsby. AS3 to Cocoa touch: JSON by Andy Jacobs.
There are other libraries you can check out as well like TouchJSON, JSONKit, Yet Another JSON Library
NSJSONSerialization does the job of converting your JSON data into usable data structures as NSDictionary or NSArray very well. I recommend it, even more because it is part of the Cocoa public interface and it is maintained by Apple.
However, if you want to map the content of your JSON to your Objective-C objects, you will have to map each attribute from the NSDictionary/NSArray to your object property. This might be a bit painful if your objects have many attributes.
In order to automatise the process, I recommend you to use the Motis category (personal project) on NSObject to accomplish it, thus it is very lightweight and flexible. You can read how to use it in this post. But just to show you, you just need to define a dictionary with the mapping of your JSON object attributes to your Objective-C object properties names in your NSObject subclasses:
- (NSDictionary*)mjz_motisMapping
{
return #{#"json_attribute_key_1" : #"class_property_name_1",
#"json_attribute_key_2" : #"class_property_name_2",
...
#"json_attribute_key_N" : #"class_property_name_N",
};
}
and then perform the parsing by doing:
- (void)parseTest
{
NSData *data = jsonData; // <-- YOUR JSON data
// Converting JSON data into NSArray (your data sample is an array)
NSError *error = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error)
return; // <--- If error abort.
// Iterating over raw objects and creating model instances
NSMutableArray *parsedObjects = [NSMutableArray array];
for (NSDictionary *rawObject in jsonArray)
{
// Creating an instance of your class
MyClass instance = [[MyClass alloc] init];
// Parsing and setting the values of the JSON object
[instance mjz_setValuesForKeysWithDictionary:rawObject];
[parsedObjects addObject:instance];
}
// "parseObjects" is an array with your parsed JSON.
// Do whatever you want with it here.
}
The setting of the properties from the dictionary is done via KeyValueCoding (KVC) and you can validate each attribute before setting it via KVC validation.
I recently had to do this. After looking at the various options out there, I threw JSONKit into my app (I found it on a JSON discussion on StackOverflow). Why?
A) It is VERY VERY simple. I mean, all it has is the basic parsing/emitting functions, what more do you need?
B) It is VERY VERY fast. No overhead - just get the job done.
I should note, I had never done JSON before - only heard of the term and didn't even know how to spell it. I went from nothing, to a working app, in about 1 hour. You just add one class to your app (the .h, .m), instantiate it, and call the parser to a dictionary object. Voila. If it contains an array, you just get the objectForKey, cast it as an NSArray. It's really hard to get simpler than that, and very fast.
For a good comparison of the speed of the different libraries for JSON parsing on iOS, take a look at The Ultimate Showdown.
-(IBAction)btn_parse_webserivce_click:(id)sender
{
// Take Webservice URL in string.
NSString *Webservice_url = self.txt_webservice_url.text;
NSLog(#"URL %#",Webservice_url);
// Create NSURL from string.
NSURL *Final_Url = [NSURL URLWithString:Webservice_url];
// Get NSData from Final_Url
NSData* data = [NSData dataWithContentsOfURL:
Final_Url];
//parse out the json data
NSError* error;
// Use NSJSONSerialization class method. which converts NSData to Foundation object.
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
// Create Array
NSArray* Response_array = [json objectForKey:#"loans"];
NSLog(#"Array: %#", Response_array);
// Set Response_array to textview.
self.txt_webservice_response.text = [NSString stringWithFormat:#"%#"
,Response_array];
}
How about NSJSONSerialization? I've been using it to parse JSON
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html

How do I get properties out of NSDictionary?

I have a webservice that returns data to my client application in JSON. I am using TouchJson to then deserialize this data into a NSDictionary. Great. The dictionary contains a key, "results" and results contains an NSArray of objects.
These objects look like this when printed to log i.e
NSLog(#"Results Contents: %#",[resultsArray objectAtIndex:0 ]);
Outputs:
Results Contents: {
creationdate = "2011-06-29 22:03:24";
id = 1;
notes = "This is a test item";
title = "Test Item";
"users_id" = 1;
}
I can't determine what type this object is. How do I get the properties and values from this object?
Thanks!
To get the content of a NSDictionary you have to supply the key for the value that you want to retrieve. I.e:
NSString *notes = [[resultsArray objectAtIndex:0] valueForKey:#"notes"];
To test if object is an instance of class a, use one of these:
[yourObject isKindOfClass:[a class]]
[yourObject isMemberOfClass:[a class]]
To get object's class name you can use one of these:
const char* className = class_getName([yourObject class]);
NSString *className = NSStringFromClass([yourObject class]);
For an NSDictionary, you can use -allKeys to return an NSArray of dictionary keys. This will also let you know how many there are (by taking the count of the array). Once you know the type, you can call
[[resultsArray objectAtIndex:0] objectForKey:keyString];
where keyString is one of #"creationdate", #"notes", etc. However, if the class is not a subclass of NSObject, then instead use:
[[resultsArray objectAtIndex:0] valueForKey:keyString];
for example, you probably need to do this for keystring equal to #"id".

Resources