I'm creating a JSON POST request from Objective C using the JSON library like so:
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#/%#/", host, action]]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json-rpc" forHTTPHeaderField:#"Content-Type"];
NSMutableDictionary *requestDictionary = [[NSMutableDictionary alloc] init];
[requestDictionary setObject:[NSString stringWithString:#"12"] forKey:#"foo"];
[requestDictionary setObject:[NSString stringWithString#"*"] forKey:#"bar"];
NSString *theBodyString = requestDictionary.JSONRepresentation;
NSData *theBodyData = [theBodyString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:theBodyData];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
When I read this request in my Django view the debugger shows it took the entire JSON string and made it the first key of the POST QueryDict:
POST QueryDict: QueryDict: {u'{"foo":"12","bar":"*"}': [u'']}> Error Could not resolve variable
I can read the first key and then reparse using JSON as a hack. But why is the JSON string not getting sent correctly?
This is the way to process a POST request with json data:
def view_example(request):
data=simplejson.loads(request.raw_post_data)
#use the data
response = HttpResponse("OK")
response.status_code = 200
return response
I have already dealt with this issue. I found a temporary solution as reading the request.body dict. I assume you have imported the json/simplejson library already.
In my view:
post = request.body
post = simplejson.loads(post)
foo = post["foo"]
This code block helped me to pass post issue. I think posting querydict in request.POST has not properly developed on NSMutableURLRequest yet.
My cruel hack to work around my problem is:
hack_json_value = request.POST.keys()[0]
hack_query_dict = json.loads(hack_json_value)
foo = hack_query_dict['foo']
bar = hack_query_dict['bar']
So this will allow me to extract the two JSON values with an extra step on server side. But it should work with one step.
Related
EDIT 2: I no longer think "_" and "/" in the dictionary keys are the problem.
EDIT 3: I don't think the JSON is the problem any more, or the HTTP POST request. I don't know exactly what it is yet.
EDIT 4: Solved it. The data element had to be valid base64, or else couch refuses it. Didn't know that. Can't select my own answer yet.
I have a dictionary I want to convert to a JSON string.
This is the setup of my dictionary:
- (NSDictionary*) createDictionary
{
NSMutableDictionary *room = [[NSMutableDictionary alloc] init];
[room setObject:self.roomNumber forKey:#"roomNumber"];
[room setObject:self.floor forKey:#"floor"];
[room setObject:self.comment forKey:#"comment"];
[room setObject:self.type forKey:#"type"];
[room setObject:[NSNumber numberWithInt:self.status] forKey:#"status"];
[room setObject:[[NSMutableDictionary alloc] init] forKey:#"_attachments"];
NSMutableDictionary *attachments = [room objectForKey:[NSString stringWithFormat:#"_%#", #"attachments"]];
[attachments setObject:[[NSMutableDictionary alloc] init] forKey:#"picture"];
NSMutableDictionary *picture = [attachments objectForKey:#"picture"];
[picture setObject:#"text/plain" forKey:#"content_type"];
[picture setObject:#"BASE64" forKey:#"data"];
NSLog(#"Room: %#", room);
return room;
}
The problem is the "_" and "/" chars. When I print out the dictionary, it looks like this:
Room: {
"_attachments" = {
picture = {
"content_type" = "text/plain";
data = BASE64;
};
};
comment = 12;
floor = 12;
roomNumber = 12;
status = 0;
type = room;
}
Don't know why it ends up on one line there, but anyway, thats not important. The thing is that the keys with "_" or "/" ends up surrounded with "-marks, and my web endpoint receiving JSON can't read it. Does anyone know how I can solve this? I use couchdb and need the _ in from of the attachments key.
EDIT: I am using it, I pass my dictionary to NSJSONSerialization, calling the function dataWithJSONObject:options:error:. I store what I get from that function in a NSData object, and set it as the http body in the request I make. Content-Type is application/json. I also set the Content-length etc. Here is the function that makes the HTTP request. createDictionary is the function above.
// Make a JSON object from the dictionary
NSError *err;
NSData* jsonRoom = [NSJSONSerialization dataWithJSONObject:[room createDictionary]
options:NSJSONWritingPrettyPrinted
error:&err];
// Set up the http POST request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#", _baseURL, #"/peters_hotell"]]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [jsonRoom length]]
forHTTPHeaderField:#"Content-length"];
[request setHTTPBody:jsonRoom];
NSLog(#"%#", jsonRoom);
// Fire the http POST request
//[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:callback];`
Right now the sendRequest call is commented out, but when I use it, the server can't accept the request.
This is the response from couchdb:
Data: {
error = badmatch;
reason = false;
}
When I leave the attachments part out of the dictionary, couch does not complain. Basically what I am trying to create is this JSON (inline attachment): http://wiki.apache.org/couchdb/HTTP_Document_API#Inline_Attachments
The thing is that the keys [...] end up being surrounded with quotation marks, and my web endpoint receiving JSON can't read it.
No, it's the opposite: what you think is JSON isn't really JSON. JSON requires the keys to be quoted, it separates keys and values using colons (:) and not an equal sign, etc.
What you currently print out is the description of the NSDictionary object and it is not JSON. It's in the legacy NeXTSTEP-style property list format.
What you want to use is the NSJSONSerialization class.
When you are posting the url and Json Object, before posting it to server encode the URL string using the
stringByAddingPercentEscapesUsingEncoding
or
NSUTF8Encoding technique..
Thanks for the help! It led me to the answer. As it turns out, my POST request was refused because my data entry wasn't valid base64, so couch refused it.
Code:
#implementation NSString (NSString_Extended)
- (NSString *)urlEncoded
{
CFStringRef urlString = CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
kCFStringEncodingUTF8 );
return [(NSString *)urlString autorelease];
}
#end
I'm developing iPhone app and manually constructing POST requests. Currently, need to compress JSON data before sending it, so looking how to tell a server the content is compressed. Setting content type header to gzip might be not acceptable because server expects JSON data. I'm looking for transparent solution, something like just to add some header telling JSON data is compressed into gzip.
I know, the standard way is to tell the server that the client accepts encoding, but you need to make GET request with accept encoding header first. In my case, I want to post the data already encoded.
Include a Obj-C gzip wrapper, for example NSData+GZip, and use it to encode the body of your NSURLRequest. Also remember to set the Content-Encoding accordingly, so the webserver will know how to treat your request.
NSData *requestBodyData = [yourData gzippedData];
NSString *postLength = [NSString stringWithFormat:#"%d", requestBodyData.length];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"gzip" forHTTPHeaderField:#"Content-Encoding"];
[request setHTTPBody:requestBodyData];
Implmenting some general Method such as follows and setting appropriate Header might help you.
// constructing connection request for url with no local and remote cache data and timeout seconds
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:callingWebAddress]];// cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:timoutseconds];
[request setHTTPMethod:#"POST"];
NSMutableDictionary *headerDictionary = [NSMutableDictionary dictionary];
[headerDictionary setObject:#"application/json, text/javascript" forKey:#"Accept"];
[headerDictionary setObject:#"application/json" forKey:#"Content-Type"];
//Edit as #centurion suggested
[headerDictionary setObject:#"Content-Encoding" forKey:#"gzip"];
[headerDictionary setObject:[NSString stringWithFormat:#"POST /Json/%# HTTP/1.1",method] forKey:#"Request"];
[request setAllHTTPHeaderFields:headerDictionary];
// allocation mem for body data
self.bodyData = [NSMutableData data];
[self appendPostString:[parameter JSONFragment]];
// set post body to request
[request setHTTPBody:bodyData];
NSLog(#"sending data %#",[[[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding]autorelease]);
// create new connection for the request
// schedule this connection to respond to the current run loop with common loop mode.
NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//[aConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
self.requestConnenction = aConnection;
[aConnection release];
I have a shopping cart in my app and an underlying data structure that I have serialized into an XML file. Im using the following code to place it on my server. However, nothing happens. Whe I check my server I donot find my file there. So I tried using just a string in place of the file and still the same. Nothing seems to be sent from the app to the server. Im running ths off the simulator.
Im wondering if there is anything wrong with this code.
CartSingleton *Cart = [CartSingleton getSingleton];
id xmlFile = [Cart serializeCart];
//Now send the xml file to the server
NSURL *url = [[NSURL alloc] initWithString:#"http://www.XXXXXXXXX.com/iOS_Files/xmlFile"];
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
[req setHTTPMethod:#"POST"];
NSData *paramData = [xmlFile dataUsingEncoding:NSUTF8StringEncoding];
[req setHTTPBody:paramData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if(theConnection)
{
NSMutableData *data = [[NSMutableData alloc] init];
self.receivedData = data;
[data release];
}
else
{
NSLog(#"Unable to make connection!");
}
I would really appreciate any help.
Thanks
See the class reference, there you can also find the links for the sample applications.
That code
if(theConnection)
{
NSMutableData *data = [[NSMutableData alloc] init];
self.receivedData = data;
[data release];
}
does not start the connection. The receivedData property will have a new NSData object, which is then changed as the response data received.
For the better understanding of the NSUrlConnection usage follow that official guide
What you can do else is to test the server w/o the application using REST Client firefox extension (just send the post request with it and see what happens).
One possible problem with the above code (their may be others also), is that the connection setup will be asynchronous. This is why you had to supply a delegate object above. Therefore, the connection is not actually made until the delegate gets a callback to say it is ready.
Have you implemented any delegate methods?
I'm rather new to trying to figure out how JSON and the DropBox API function with the iphone SDK that DB has release for the iPhone. I understand how both technologies work in general and that you have to build a dictionary into a request and get another dictionary back, but I can't find a solid example of how to do anything specifically enough online in Objective C.
I just need one example of how to retrieve for instance the user's profile information by creating a JSON request to fetch info from the drop-box server.
I've been able to log the user in and linking the device using the consumer key and consumer secret but what's next I'm a little at a loss.
MANY thanks for any guidance or examples.
To send your data
Example for POST methods:
url = #"https://api.dropbox.com/<version>/account/"
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:value forKey:#"callback"];
Example for GET methods (URL queries):
NSString *urlString = [[[NSString alloc] initWithString:#"https://api.dropbox.com/<version>/account/info
"] autorelease];
urlString = [urlString stringByAppendingFormat:#"?callback=whatever&status_in_response=something"];
NSURL *url = [[NSURL alloc] initWithString:urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDidFinishForThreadID:)];
[request startAsynchronous];
To retrieve JSON values and convert them into Dictionary
SBJsonParser *json = [[SBJsonParser alloc] init];
NSDictionary *dict = (NSDictionary*)[json objectWithString:responseString];
You will need JSON Framework: http://code.google.com/p/json-framework/
And also ASIHTTPRequest: http://allseeing-i.com/ASIHTTPRequest/
Haven't tested it with dropbox, but should be this way.
I just wanted to ask you if anyone can help me parsing the returned data from the Twitpic API?
I'm creating a HTTPFormRequest using the ASIHTTPRequest Wrapper for Cocoa. This all happens in an iPhone application:
NSURL *url = [NSURL URLWithString:#"http://twitpic.com/api/upload"];
NSString *username = t_user;
NSString *password = t_pass;
NSData *twitpicImage = UIImagePNGRepresentation(imageView.image);
// Now, set up the post data:
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:twitpicImage forKey:#"media"];
[request setPostValue:username forKey:#"username"];
[request setPostValue:password forKey:#"password"];
[request setData:twitpicImage forKey:#"media"];
// Initiate the WebService request
[request start];
if ([request error]) {
NSLog(#"%#", [request error]);
} else if ([request responseString]) {
NSLog(#"%#", [request responseString]);
}}
Now comes the hard part, I don't know how to parse the data that is in [request responseString]. I know I need to use NSXMLParser, but I dunno how to use it. All I need is to get the url of the image.
Thx in advance.
Feel free to have a look at my little XML parse classes here http://www.memention.com/blog/2009/10/31/The-XML-Runner.html
I have started to use them for parsing the response from image upload to yfrog.com
Basically I do like this...
In NameValueParser.m I changed the entry tag to rsp like this
entryName = [[NSString stringWithString:#"rsp"] retain];
then where the response has been received I parse it like this
NameValueParser *parser = [NameValueParser parser];
[parser addFieldName:#"statusid"];
[parser addFieldName:#"userid"];
[parser addFieldName:#"mediaid"];
[parser addFieldName:#"mediaurl"];
[parser addFieldName:#"err"];
[parser parseData:responseData]; // the response received by ASIHTTPRequest
NSArray *rspArray = [parser list];
NSLog(#"%#", rspArray); // Have a look at it here
Try it as written at the bottom of this tutorial click here using NSScanner. They are showing exactly what you need, retrieving only the mediaurl = URL of uploaded image.
NSScanner *scanner = [NSScanner scannerWithString:responseString]; ...
GSTwitPicEngine does XML and JSON parsing both: http://github.com/Gurpartap/GSTwitPicEngine
Though, why not use JSON format for the Twitpic API responses? It's easy to parse and deal with using yajl, TouchJSON, json-framework or other Cocoa JSON libraries