sending JSON using simple RKClient post - objective-c

I'm trying to send a request using RestKit. I want it to be sent in JSON, but it seems that default post methot from RKClient uses some kind of FORM data formatting (I check it using [request HTTPBodyString]).
I need to send the data using simple POST, like this (I'm not using object mapping):
[[RKClient sharedClient] post:path usingBlock:^(RKRequest* req) {
req.params = params;
req.delegate = self;
}];
I found some solutions that use NSJSONSerialization, but that method crashes when I put NSDate object in my JSON dictionary.
Is there a way to tell the RKClient to send requests using JSON?
Update: params is a NSDictionary. I want to be able to tell RestKit to serialize my dictionary using JSON when sending it by POST.

You can do this by setting the RestKit's serializationMIMEType property to RKMIMETypeJSON.
E.g.
[RKObjectManager sharedManager].acceptMIMEType = RKMIMETypeJSON;
[RKObjectManager sharedManager].serializationMIMEType = RKMIMETypeJSON;
see also http://restkit.org/api/master/Classes/RKObjectManager.html:
The desired serialization format is configured by setting the
serializationMIMEType property. RestKit currently supports
serialization to RKMIMETypeFormURLEncoded and RKMIMETypeJSON.
Update:
Example for posting a plain JSON String with the correct Mime Type:
RKParams *params = [RKRequestSerialization serializationWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
MIMEType:RKMIMETypeJSON];
[[RKObjectManager sharedManager].client post:#"/mypath"
params:params
delegate:self];
Update 2:
Getting JSON from a NSDictionary object:
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:myNSDictionaryObject
options:NSJSONWritingPrettyPrinted
error:&error];
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

Related

Rails params not being constructed correctly

I have a strange issue.
I have tried a lot of different things to reproduce the issue but it has not been possible. The error shows up thanks to exception_notification.
I am sending a NSDictionary to the backend, and instead of being parsed as a dictionary, this is what I get (on the rails side):
* Parameters : {"{\"facebook_id\":\"\",\"city\":\"\",\"account_type\":\"email\",\"poll_reminder\":\"0\",\"last_name\":\"\",\"picture_url\":\"\",\"email\":\"\",
\"birthday\":\"\",\"users_value_categories_attributes\":"=>{"{\"value_category_id\":25},{\"value_category_id\":24},
{\"value_category_id\":21},{\"value_id\":24,\"priority\":14},{\"value_id\":21,\"priority\":15},
{\"value_id\":58,\"priority\":16},
{\"value_id\":8,\"priority\":17},{\"value_id\":9,\"priority\":18},{\"value_id\":41,\"priority\":19},
{\"value_id\":27,\"priority\":20}"=>{",\"first_name\":\"\"}"
=>nil}}}}}
It is dictionary where the only key is my serialized dictionary!
This is the error on the rails side:
An ActiveRecord::UnknownAttributeError occurred in #:
unknown attribute: {"facebook_id":"","city":"","account_type":"email","poll_reminder":"0","last_name":"","picture_url":"","email":"","birthday":"",":...........
app/api/v2/resources/example_business_api.rb:6:in `block in <class:ExampleBusinessApi>'
params hash is like:
{"<MY SERIALIZED DICTIONARY" => nil }
instead of being the dictionary itself.
The relevant code in iOS is:
request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[self getHTTPBodyForUser:theUser]];
getHTTPBodyForUser is the following:
+ (NSData *)getHTTPBodyForUser:(User *)user {
// Load a tmp dictionary using user variable
....
// The tmp dictionary is totally fine
NSError *error;
NSData *postdata = [NSJSONSerialization dataWithJSONObject:tmp options:0 error:&error];
return postdata;
}
The weird part is that just some users are having the error.....
Any help?

How to serialize NSManagedObject to JSON in restkit 0.20?

How to serialize NSManagedObject to JSON in restkit 0.20 using inverse mapping?
Right now I don't need to post anything anywhere.
I would like manually create object MyObjectManaged.
Set some attributes for example:
id,
name,
age
Map them with existing mapping my mapping to JSON attributes:
userid,
first_name,
age
create and print JSON.
Is it possible? When yes, how?
Thank you in advance for your answer.
I've recently been trying to do the same thing :) I wanted to keep the mappings so that eventually I can hook up to a server, but also reuse them for serializing objects out to a file.
I did this using the inverseMapping and running it through an RKMappingOperation.
First set up your mappings from JSON -> Core Data Object
RKEntityMapping mapping = [RKEntityMapping mappingForEntityForName:#"MyManagedObject" inManagedObjectStore:rkManagedObjectStore];
[self.nodeMapping addAttributeMappingsFromDictionary:#{
#"userid": #"id",
#"first_name": #"name",
#"age": #"age"
}];
Then use the inverse mapping to map your object instance (e.g. "myObject") to a dictionary:
NSMutableDictionary *jsonDict = [NSMutableDictionary dictionary];
RKObjectMappingOperationDataSource *dataSource = [RKObjectMappingOperationDataSource new];
RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:myObject
destinationObject:jsonDict
mapping:[mapping inverseMapping]];
operation.dataSource = dataSource;
NSError *error = nil;
[operation performMapping:&error];
Assuming there's no error, you can then serialize the dictionary:
NSData *data = [RKMIMETypeSerialization dataFromObject:jsonDict
MIMEType:RKMIMETypeJSON
error:&error];
Not sure what you wanted to do with it from there, but if you wanted to print it to a string you could do:
NSString *jsonString = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]
Hope that helps
John Martin answer seems to work but I got the problem that NSManagedObject instances with a NSNumber property that is set with
[NSNumber numberWithBool:boolvalue]
serializes a json as value 1/0 instead of true/false.
Our backend could not handle numbers as booleans.
I solved this with using the RestKit built in class: RKObjectParameterization
Using the follow method my NSManagedObjects were properly serialized when there was an NSNumber property that was set as a bool.
+ (NSString *)getJsonObjectWithDescriptor:(RKRequestDescriptor *)requestDescriptor objectToParse:(id)objectToParse {
NSError *error = nil;
NSDictionary *jsonDict = [RKObjectParameterization parametersWithObject:objectToParse requestDescriptor:requestDescriptor error:&error];
NSData *data = [RKMIMETypeSerialization dataFromObject:jsonDict
MIMEType:RKMIMETypeJSON
error:&error];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
You can head over to the RestKit wiki and have a look in the object mapping. In the paragraph "Object Parameterization & Serialization" you'll find the information about the serialization and inverse mapping.

How to specify Restkit object manager to perform a POST

I am trying to send a POST request using ios Restkit. I can perform a GET, but cannot find how to send a POST.
My current code looks like the following:
RKURL *URL = [RKURL URLWithBaseURL:[objectManager baseURL] resourcePath:#"/users/sign_in.json" queryParameters:params];
[objectManager loadObjectsAtResourcePath:[NSString stringWithFormat:#"%#?%#", [URL resourcePath], [URL query]] delegate:self];
Apparently, this performs a GET. Any idea what I should add to make it a POST?
Thanks!
You can configure your RKRequest with:
[request setMethod:RKRequestMethodPOST];
To answer your question (regardless of the above discussion) you can do the following:
[objectManager loadObjectsAtResourcePath: #"path" usingBlock: ^(RKObjectLoader *loader) {
loader.HTTPMethod = RKRequestMethodPOST;
}];

How to create JSONP on MacOS?

I use the following code to create a JSON file.
// Some data in keys and vals.
NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:vals forKeys:keys];
NSError* writeError = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:NSJSONWritingPrettyPrinted error:&writeError];
NSString* path = #"json.txt";
[jsonData writeToFile:path atomically:YES];
How can I output a JSONP file? Is there a Cocoa framework I can use?
Update: In the meantime, I used a quick-and-dirty solution: I read in the JSON file just written before to the disc and add the missing JSONP-function to the string. Then, I write the file a second time. I think that's not worth being the answer to my question. So I will leave this question open to a smarter solution.
You could convert the JSON data to a string, wrap it in your function call and then write it to a file. Example:
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:NSJSONWritingPrettyPrinted
error:NULL];
NSMutableString *jsonString = [[[NSMutableString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding] autorelease];
[jsonString insertString:#"functionCall(" atIndex:0];
[jsonString appendString:#");"];
[jsonString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
(I'm using a mutable string here for better memory efficiency.)
I don't know objective-c or Cocoa. (I use python on MacOS to create JSNOP responses), but it's a simple thing to do.
The basic idea is to wrap the JSON data in a javascript function call:
functionCall({"Name": "Foo", "Id" : 1234, "Rank": 7});
The tricky part is that the function name, "functionCall", is set by the browser and AFAIK the name of that query parameter is not standardized. jQuery uses jsonCallback. Other's use json or callback. So the request url must be checked for that callback name and that function name must be used to wrap the json data.

AFNetworking post request not getting through to Sinatra app

I'm trying to post a request from my iPhone with a Sinatra API I made. Currently all my Sinatra app is doing is printing out the request that has been sent to it. This is the code for that:
post '/profile' do
puts "#{params}"
end
My objective-c is pretty simple as well. All it does is send a post request to my API:
NSURL *url = [NSURL URLWithString:kBaseURLString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:JSON, #"json", nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"/profile" parameters:dictionary];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"SUCCESS");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[operation start];
When JSON (in line 3 of the obj-c) is a very short string, such as #"test", Sinatra prints it out correctly like this:
{"json"=>"test"}
When I use the actual JSON profile data, which is a very long JSON blob, Sinatra prints it out as this:
{"json"=>"(null)"}
I can't figure out why the long blob is getting through. I am 100% sure that i'm passing the correct string, but Sinatra is not receiving it. My current theory is that Sinatra has a max character limit on requests, but I am new to Sinatra and Ruby, and I have no idea how I'd test that. What is going wrong?
UPDATE:
First off, thanks Kjuly for your suggestions. I figured out that I was wrong on the character limit on Sinatra thing. In obj-c I'm doing a log of the dictionary that has the JSON blob on line 3, and it has the json blob. However, when I log the body of the NSMutableURLRequest on line 4, the body is empty. When I use my tiny JSON blob, the body is filled.
Does NSMutableURLRequest have a character limit? Can anyone think of a reason why it would not accept my very large dictionary with the large JSON blob, but not with the small one.
Thanks!
UPDATE AGAIN:
The request body now fills correctly. I had to add this line into line 3:
[httpClient setParameterEncoding:AFJSONParameterEncoding];
Now I am getting this response back in the HTTPResponse from Sinatra:
Error Domain=com.alamofire.networking.error Code=-1016 "Expected content type {(
"text/json",
"application/json",
"text/javascript"
)}, got text/html"
Sinatra is now just printing
{}
Instead of {"json"=>"(null)"}
Still not sure what's going on.
Update 3
Okay, what I thought was the HTTPResponse from Sinatra - the text/json stuff - was because I was returning a text/html from Sinatra back in AFNetworking. I have now checked the body that Sinatra is receiving, and my giant JSON blob is in there. However, "params" is still empty.
Anyone have any idea why?
FIXED IT
Looks like when you post JSON to sinatra you have to read the body of the request directly. In Sinatra, you do this like so:
profile = JSON.parse(request.body.read.to_s)
Then profile is your parsed object.
I think you need to use AFJSONRequestOperation instead, here's a sample code:
// Fetch Data from server
NSURL *url = [NSURL URLWithString:#"https://gowalla.com/users/mattt.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation * operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest * request, NSHTTPURLResponse * response, id JSON) {
NSLog(#"Name: %# %#", [JSON valueForKeyPath:#"first_name"], [JSON valueForKeyPath:#"last_name"]);
}
failure:nil];
[operation start];
Or you can visit the WIKI PAGE, see Step 4: Download and Parse JSON.