I was to post some of my COre Data objects back to a web service and would like to send them as JSON. I am receiving objects from the server a JSON using this library:
http://code.google.com/p/json-framework/
But I cannot figure out how to change my objects back to JSON?
To create json from you r objects, you have to build an NSDictionary from your object, and then convert to string with the SBJsonWriter class.
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObject:(NSArray *)YourArrayOfElements forKey:#"objects"];
SBJsonWriter *jsonWriter = [SBJsonWriter new];
//Just for error tracing
jsonWriter.humanReadable = YES;
NSString *json = [jsonWriter stringWithObject:jsonDictionary];
if (!json){
NSLog(#"-JSONRepresentation failed. Error trace is: %#", [jsonWriter errorTrace]);
}
[jsonWriter release];
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
And then you can set as your post request's body.
If you would like a more full-featured solution that what is offered by a standalone parsing library, you may want to take a look at RestKit: http://restkit.org/
The framework wraps the operations of fetching, parsing, and mapping JSON payloads into objects. It also allows you to update remote representations by POST/PUT'ing the objects back with a request. By default, outbound requests are form-encoded but the library ships with a class for using JSON as the wire format for posting back to the server.
At a high level, here's what your fetch & post operations would feel like in RestKit:
- (void)loadObjects {
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[#"/path/to/stuff.json" delegate:self];
}
- (void)objectLoader:(RKObjectLoader*)loader didLoadObjects:(NSArray*)objects {
NSLog(#"These are my JSON decoded, mapped objects: %#", objects);
// Mutate and PUT the changes back to the server
MyObject* anObject = [objects objectAtIndex:0];
anObject.name = #"This is the new name!";
[[RKObjectManager sharedManager] putObject:anObject delegate:self];
}
The framework takes care of the JSON parsing/encoding on a background thread and let's you declare how attributes in the JSON map to properties on your object. Mapping to Core Data backed classes is fully supported.
Related
I'm trying to call a RESTful web service with RESTKit 0.2 that returns only a string, but the RKResponseDescriptor class forces me to use a mapping with its method responseDescriptorWithMapping in order to be able to get the string value, I created the mapping and got the string value without any problems, but how can i get this string value without having to create the object mapping (i.e. creating an NSObject subclass, creating a property for the string to be received, and creating a mapping dictionary between the returned JSON key and this property) ?
You can still use RestKit to make the request. Sometimes it makes sense to do so in case server is returning an error for example, in which case it is necessary to access the raw data returned from server.
[[RKObjectManager sharedManager] getObjectsAtPath:kLoginURL
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:operation.HTTPRequestOperation.responseData options:NSJSONReadingMutableLeaves error:&error];
NSLog(#"msg: %#", [json objectForKey:#"msg"]);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
In this case, don't use RestKit to make the request. RestKit uses AFNetworking for the underlying network communications so if you import the classes you can use it too. Try directly using AFHTTPRequestOperation to make the request.
I have a JSON object like
{"status":"200","id":"23","username":"nipinponmudi#gmail.com","fname":"hh","laname":"hh","timezone":"2","createdate":"2011-09-20 22:05:24","key":"db3f57a8f2b9abd9d51916232f5a77b9"}
The object above is a response from a server.I want to display these objects in corresponding textfields on the viewdidload method.I want to read this object and display separately in textfields.I need to extract the username,fname,lname only.No need to read the status
take a look at https://github.com/stig/json-framework/ . I'm sure they are so many source and example out there to show you.
NSString *jsonString = #{"status":"200","id":"23","username":"nipinponmudi#gmail.com","fname":"hh","laname":"hh","timezone":"2","createdate":"2011-09-20 22:05:24","key":"db3f57a8f2b9abd9d51916232f5a77b9"}";
SBJsonParser *parser = [SBJsonParser new];
id object = [parser objectWithString:jsonString];
txtName.text = [object objectForKey:#"username"];
The goal is to have a singleton data controller class called FetchData.h/.m that pulls data using ObjectiveFlickr ( https://github.com/lukhnos/objectiveflickr ).
FetchData.m grabs the data with this:
OFFlickrAPIContext *context = [[OFFlickrAPIContext alloc] initWithAPIKey:YOUR_KEY sharedSecret:YOUR_SHARED_SECRET];
OFFlickrAPIRequest *request = [[OFFlickrAPIRequest alloc] initWithAPIContext:context];
// set the delegate, here we assume it's the controller that's creating the request object
[request setDelegate:self];
[request callAPIMethodWithGET:#"flickr.photos.getRecent" arguments:[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"per_page", nil]]
and then implement the following delegate:
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary;
Currently I have this code to save the NSDictionary as a property list to a file as an alternative to a singleton:
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse: (NSDictionary *)inResponseDictionary{
if([inResponseDictionary writeToFile:#"inResponseDictionary.xml" atomically:TRUE])
{
NSLog(#"%#", [[NSFileManager defaultManager] fileExistsAtPath:#"inResponseDictionary.xml"]);
}
}
When I read this file back, I get Null. The file is read back as such:
NSDictionary *inResponseDictionary = [NSDictionary dictionaryWithContentsOfFile:#"inResponseDictionary.xml"];
NSDictionary *photoDict = [[inResponseDictionary valueForKeyPath:#"photos.photo"] objectAtIndex:0];
NSLog(#"%#", [photoDict count]);
Is there a better way to store this data from ObjectiveFlickr so that it can be accessed by other classes and view controllers? Or is there a better way to implement this in the View Controller.
What is in the returned NSDictionary? Are you sure they are all valid plist objects? The photo data might need to be modified (say, base 64 encoded into an array) before your write will work.
The docs for NSDictionary writeToFile: say
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
As for the singleton aspect - will you be making more than one of these calls at a time? Is there a need to persist the data? If no & no, just keep the dictionary in memory. If you have multiple calls happening at once, you'll need another layer of abstraction (some indexing) to put each call's results in it's own unique location. And that's not happening with your current implementation.
I am trying to use Json in my iphone projects ,
but i didnt get how can I start using json in my project.
help me out from this condition.
Thanks in advance.
Well if you want you can get started by this
http://iosdevelopertips.com/networking/iphone-json-flickr-tutorial-part-1.html
this tutorial will help you understand what json does, but if you want to started on using it in your code than you should use the following example:
April 26, 2009
Dealing with JSON on iPhone
You can easily use the JSON (JavaScript Object Notation) data format in client-server communications
when writing an iPhone app. This blog is not suggesting that JSON is a more superior format for data
exchange than its counterparts such as XML. In fact, we have many projects that don't use JSON.
However, handling JSON is relatively straight forward in ObjectiveC.
Unfortunately, Apple iPhone SDK (as of this writing, the latest is iPhone 2.2.1) doesn't come with
a built-in JSON parser. But I found out a good one called json-framework. It is both a generator
and a parser. As a generator, json-framework can create JSON data from an NSDictionary. As a parser,
you can pass to json-framework an NSString that consists of JSON data and it will return a
NSDictionary that encapsulates the parsed data.
Next, I'm going to show you several examples. Before you proceed, download the library and make
sure you add it to your SDK path list (see INSTALL file that comes with it). If setup properly,
you should be able to start using the library by importing its header file:
#import "JSON/JSON.h"
Consider the following code:
NSDictionary *requestData = [NSDictionary dictionaryWithObjectsAndKeys:
#"grio", #"username",
#"hellogrio", #"password",
nil];
This instantiates a new dictionary which we'll turn into a JSON string. To do so, you'll need to
use a function called JSONRepresentation. This function is added as a category to NSObject. It
means that as long as there's an import of JSON.h file, you can call the function on any NSObject
object.
NSString* jsonString = [requestData JSONRepresentation];
And this is what you got when you print (NSLog(#"%#", jsonString);):
{"username":"grio","password":"hellogrio"}
Parsing is just as simple. Consider the following JSON data:
{
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{
"value": "New",
"onclick": "CreateNewDoc()"
},
{
"value": "Open",
"onclick": "OpenDoc()"
},
{
"value": "Close",
"onclick": "CloseDoc()"
}
]
}
}
}
Assume that this is the data that you received from a web service called and is currently stored
in an NSString called jsonResult. To parse it, you need to create SBJSON object and call one of
its initialization method, objectWithString.
SBJSON *json = [[SBJSON new] autorelease];
NSError *jsonError;
NSDictionary *parsedJSON = [json objectWithString:jsonResult error:&jsonError];
If parsing fails for reasons such as invalid construct of JSON format, jsonError variable will
be filled with the error info. If it is successful, parsedJSON will contain keys whose values
are either an NSString or NSDictionary. Let's look at the inside of parsedJSON:
NSDictionary* menu = [parsedJSON objectForKey:#"menu"];
NSLog(#"Menu id: %#", [menu objectForKey:#"id"]);
NSLog(#"Menu value: %#", [menu objectForKey:#"value"]);
And here's the output:
Menu id: file
Menu value: File
Observe the JSON data again. popup is an NSDictionary which has an array of menuitem.
NSDictionary* popup = [menu objectForKey:#"popup"];
NSArray* menuItems = [popup objectForKey:#"menuitem"];
NSEnumerator *enumerator = [menuItems objectEnumerator];
NSDictionary* item;
while (item = (NSDictionary*)[enumerator nextObject]) {
NSLog(#"menuitem:value = %#", [item objectForKey:#"value"]);
}
And this is the output:
menuitem:value = New
menuitem:value = Open
menuitem:value = Close
Sorry i forgot the link to this website
json-framework is also good if you don't like TouchJson
I use ASIHttpRequest to make an Asynchronous call to my Web Service. In my requestFinished method I parse the JSON I received from my call and from there you can do pretty much anything with the JSON you received.
I'm using TouchJSON to parse the output of a JSON Rails API, but am having difficulties. The overall goal is to loop through the response, parse the JSON, create a Round instance for each JSON object, and stick those Round objects into an NSArray so I can load this into a UITableView. So if there's a more straight-forward way to do that than what I'm about to show (which currently is NOT working, btw) please let me know.
The Rails API is returning a collection that looks something like this:
[
{
"round": { "course_title": "Title A", "result": "+8" }
},
{
"round": { "course_title": "Title B", "result": "+4" }
},
...
]
I'm also using ASIHTTPRequest and I can successfully get the response using:
NSString *responseString = [request responseString];
But from there, I cannot seem to get anywhere. Here's more-or-less what TouchJSON suggests:
NSString *jsonString = [request responseString]; // [{"round":{...}}, ..., {"round:{...}}]
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSDictionary *dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:nil];
// then I do this...
NSLog(#"JSON: %#", dictionary); // JSON: null
I thought from there I would be able to loop through the dictionary and create the object mappings using my Round class. But maybe that's the wrong approach altogether.
My thoughts are that the JSON being returned from Rails is an array of JSON objects, so maybe that's why the JSON parser doesn't recognize it as valid JSON? From this, I have two questions:
1) Should TouchJSON be able to accept an array of JSON objects like what my API is returning?
2) Is it possible to cast the responseString to an NSArray so I can loop through each "round" and parse the JSON that way? If I remove the first and last characters from the response string (i.e. "[" and "]") the JSON parser will only grab the first "round" in the collection.
3) Am I going about this whole process correctly?
Any tips/advice would be much appreciated.
TouchJSON presents three main ways to go from JSON to an Obj-C object. They are all present in the header for CJSONDeserializer which you're already using:
- (id)deserialize:(NSData *)inData error:(NSError **)outError;
- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError;
- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError;
The first one will return return whatever, either a dictionary, array, string or whatever the root type of the JSON is.
The other two expect a dictionary or an array and will complain (i.e. return nil and give you an NSError) if they don't get the right data.
The deserializeAsDictionary:error: method of CJSONDeserializer relies on the scanJSONDictionary:error: method of CJSONScanner. This method expects the "dictionary" to be an object literal. Therefore, your data must start with a {. Since your data is an array, you would want to use the deserializeAsArray:error: method of CJSONDeserializer.
Read the documentation carefully, your code is incorrect. It should look like this:
NSData *jsonData = [request responseData]
NSArray *rounds = [[CJSONDeserializer deserializer] deserialize:jsonData error:nil];
// then I do this...
NSLog(#"JSON: %#", rounds);
You could also have used:
NSArray *rounds = [[CJSONDeserializer deserializer] deserializeAsArray:jsonData error:nil];
However your absolute BIGGEST mistake was passing nil for error. You could have avoided going to stackoverflow at ALL if you had passed something in for NSError and then checked that.
With the right tools, this is WAY simpler than you're making it. I do this sort of thing all the time.
Use Stig's JSON framework, and import the NSString category that provides the JSONValue method.
Then inside your ASIHTTPRequest response handler code, go thusly:
NSMutableArray *roundlist = [NSMutableArray array];
NSArray *results = [[request responseString] JSONValue];
for (NSDictionary *item in results) {
Round *myRound = [item objectForKey:#"round"];
//don't actually do the above. Do whatever you do to instantiate a 'Round'.
[roundlist addObject:myRound];
}
[self.tableView reloadData];
EDIT: Geezo. Objection noted re valueForKey: vs objectForKey:. I updated my code sample, and I think we all learned something here.
I also didn't mean any offense with the phrase "with the right tools". OP was looking to simplify his code, and the RIGHT TOOL for that is the library with the simplest interface. I have nothing against TouchJSON per se, but JSON Framework has the simpler interface.