Rails params not being constructed correctly - objective-c

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?

Related

Objective C - JSON data unavailable after being returned correctly

I'm trying to put in a tableview the result of a JSON that is returned correctly.
The problem is that I can only access the data within the block where the information is returned, even assigning the result to a instance variable.
My code is as follows:
NSURL *url = [NSURL URLWithString:#"http://www.my_url.com.br/app/?type=list&info=15,16&lat=some_data&long=some_data"];
ASIHTTPRequest *_request = [ASIHTTPRequest requestWithURL:url];
ASIHTTPRequest *request = _request;
request.requestMethod = #"POST";
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request setDelegate:self];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
NSDictionary *root = [NSJSONSerialization JSONObjectWithData:request.responseData options:0 error:nil];
self.data = [root objectForKey:#"listing"];
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
}];
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Error: %#", error.localizedDescription);
}];
[request startAsynchronous];
NSLog(#"Data only: %#", data); //At this point, the "data" content is nil
This is my "ListDataController.h" file definitions :
#interface ListDataController : UITableViewController{
ApplicationCell *tmpCell;
NSArray *data;
UILabel *status ;
UINib *cellNib;
}
#property (nonatomic, retain) IBOutlet ApplicationCell *tmpCell;
#property (nonatomic, retain) NSArray *data;
The JSON returned :
Data self: (
{
Icon = "Baseball.png";
Name = Baseball;
NumRatings = 106;
Price = "$2.98";
Publisher = "Super Sportz, Inc.";
Rating = "3.5";
},
{
Icon = "Checkers.png";
Name = Checkers;
NumRatings = 87;
Price = Free;
Publisher = "Gameitoids, Inc.";
Rating = 4;
}
)
The question is : why I can not access the instance variable "data" outside the block, even assigning it the result of json?
You can access data outside the block, but it is set to nil. You are probably setting data properly, just not at the moment you thought.
By the time you execute the final log, the completion block has not run yet. The request is asynchronous, meaning the completion block will execute at some point in the future. This is a key concept to understand when working with blocks.
UPDATE:
To procress the data once it has been retreived, you can call the desired from the completion block:
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
NSDictionary *root = [NSJSONSerialization JSONObjectWithData:request.responseData options:0 error:nil];
self.data = [root objectForKey:#"listing"];
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
[self handleData:self.data]; // you would have to define this method your self
}];
One thing to consider, if you are updating the UI based on self.data, you would want to make sure you execute handleData on the main thread.
Some Links:
Objective C Blocks
Concurrency Programming Guide
timing (like the 2 answers above)
to write into variables living outside the block you have to use the __block directive before the variable declaration. I am not sure if instance variables are special to this, but to be sure you could use some temp variable for storing the result before assign it finally to the ivar.
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
Fine. What is your problem? This is the completion block. It is executed when all data was received. Any access to data before that point in time will end up in nil in its best case or outated/wron/unpredictable data in its worst case.
What happens here is your log
NSLog(#"Data only: %#", data); //At this point, the "data" content is nil
will be executed well before getting the data.You are using asynchronous request using block that is the execution will happen on a seperate thread without affecting the main thread execution
and on completion of the seperate thread the completion block get executed .Hence response only loads into data at this point only
For better understandability
Put breakpoints in both log and completion block
You can see the execution on the log happens before the completion block
EDIT :
you can do the operations in the completion block after response assigned to the data
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
NSDictionary *root = [NSJSONSerialization JSONObjectWithData:request.responseData options:0 error:nil];
self.data = [root objectForKey:#"listing"];
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
//DO here WHATEVER you want to do after getting response
//eg:
[table reloadData]
}];
The request when get all the data will execute the completion block.So whatever you want to do after getting the response can be done in the completion block

JSON Payload doesnt seem to be sending

My problem I'm pretty positive is simple, I must just be missing something.. just not sure what.
I can send GET and POST for granular elements (this=that kind of stuff), but a web service call I need to send data too, takes a raw JSON block, with no "key"
Heres the method I wrote:
-(NSData *)execute {
// Smart Chooser ?
if(PostData.count >0 || Payload != nil)
[self setMethod:UPLINK_METHOD_POST];
else
[self setMethod:UPLINK_METHOD_GET];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.connectionUrl
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
if([UPLINK_METHOD_GET isEqualToString:self.connectionMethod])
[request setHTTPMethod:#"GET"];
else
[request setHTTPMethod:#"POST"];
NSString *gData = [self compileGetData];
NSString *pData = [self compilePostData];
// if we have get data, set it into the URL string
if(GetData.count > 0) {
[self setURLWithString:[[self.connectionUrl absoluteString] stringByAppendingString:[#"?" stringByAppendingString:gData]]];
[request setURL:self.connectionUrl];
}
// if we have post data, set it in the body
if(PostData.count > 0) {
const char *bytes = [[NSString stringWithString:pData] UTF8String];
[request setHTTPBody:[NSData dataWithBytes:bytes length:strlen(bytes)]];
}
// Override any post data if a payload is already defined.
if(Payload != nil) {
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"URL : %#", request.URL);
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if(err != nil)
NSLog(#"here was an error: %#", err);
return responseData;
}
-(NSDictionary *)executeAsJSON
{
NSData *responseData = [self execute];
NSError *e;
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&e];
}
Ok SO, the way this thing works, is that it automatically sets whether the request is POST or GET depending on the data provided in the GetData, PostData, and Payload vars.
The request is GET by default, but turns into POST if PostData or Payload have anything in them.
The compileGetData and compilePostData mostly just bring back formatted strings with arrays of information combined, nothing special there.
But thats not where the problem is.
See, "Payload" overrides anything "PostData" had in it. If you had provided PostData elements into the class, it would just be overridden by a provided Payload if that does exist.
I needed to provide this to demonstrate the "workarea" as it exists right now, its not linearly provided information.
This is the area of interest:
// Override any post data if a payload is already defined.
if(Payload != nil) {
//const char *plbytes = [[NSString stringWithString:Payload] UTF8String]; // this didn't work
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]]; // inline, doesn't work either
}
When I say "doesnt work", what I mean is, im getting back an error JSON array from the webservice that basically means "hey, wheres the payload?". If the request is not POST it comes back as a general error, so thats all working, the URL is then obviously correct.
I've used RESTConsole for Chrome to test the webservice to make sure its working properly, and it does.
I've also checked through the debugger the exact payload im sending, i copy+pasted that into RESTConsole, and it works there.
I'm.. honestly at a loss here...
Try using a web proxy like Charles or Wireshark (I personally preferr Charles due to it's ease of use, it's a 30-day trial though) and monitor the request you make from RESTConsole and the one you make from your app and see if they look the same.
Check any headers, line returns and anything else that looks different.
That's the best I can think of to start with

JSON Parsing Error in ios

I want to fetch some data from some url and print result in nslog.
I am passing URl and want to fetch result in log only.
I have used this code :
-(void)GETJSONDATA
{
NSString*lu=#"tmp";
NSString *requestString = [[NSString alloc]init];
// [[NSUserDefaults standardUserDefaults] setValue:nil forKey:#"WRONGANSWER"];
NSLog(#"request string:%#",requestString);
NSData *requestData = [NSData dataWithBytes: [requestString UTF8String] length: [requestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString: #"http://assessments.tlisc.org.au/webservices/questions/getbytaskpart.php?jsoncallback=?&token=1726204214321678|xTAieBBJoDaWmBsG1stxfq4zLO4&taskpartid=1"]];
NSString *postLength = [NSString stringWithFormat:#"%d", [requestData length]];
[request setHTTPMethod: #"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:requestData];
NSError *respError = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: &respError ];
if (respError)
{
// NSString *msg = [NSString stringWithFormat:#"Connection failed! Error - %# %#",
// [respError localizedDescription],
// [[respError userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Test" message:#"check your network connection" delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
else
{
NSString *responseString = [[NSString alloc] initWithData:returnData encoding: NSUTF8StringEncoding];
NSLog(#"Resp : %#",responseString);
NSDictionary *results = [responseString JSONValue];
NSLog(#"results=%#",results);
}
}
It is showing me this error :
-JSONValue failed. Error trace is: (
"Error Domain=org.brautaset.JSON.ErrorDomain Code=11 \"Unexpected end of string\" UserInfo=0x6a42be0 {NSLocalizedDescription=Unexpected end of string}"
Can anybody point me error?
In response string , it is showing null value.
I don't know the actual error.
it is working fine in browser but when I parse it it is showing this error....Is there anyway through which I can modify the url and get result ...i have checked my code with different url.and it is working proper..
I think I was using the same library as you're using here, and for some unexplained reason it just failed on a particular google api, sometimes getting results but usually failing. There appeared to be nothing wrong with the code.
In the end I went with the inbuilt NSJSONSerialization ( http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html ) object and it hasn't failed since. I wasn't even aware there was JSON support built in (from iOS5 apparently).
I ran the two calls and processed them side by side, the external library continually failed. The internal methods worked fine.
Since I'm a beginner and the docs (above) don't really help me a lot, I used this tutorial http://www.raywenderlich.com/5492/working-with-json-in-ios-5 to get my head around it
As the error told you: the incoming JSON is not well-formed. Making a simple GET request to the URL you are using, I'm getting a JSON that is malformed, i.e. it is not valid JSON. I guess posting data to this URL returns the same format with actual data in it (not null).
Making use of a simple JSON Validator, I'm getting this
Parse error on line 5: ... "Description": "\n\tQuestion Numbe
-----------------------^ Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '['
Check your url in a browser. See the result. Your result's Structure should be like the same as JSON. if there is any other extra word in start of the json means. you should check your php code.
You have got HTML tags within your strings. Paragraph marks like
<p> and </p>
And these are not properly escaped. However, I do not know by hard wether the less than sign and greater than sign need to be escaped in JSON but apparently they are creating the issue.
Using google I find statements for both. However, some site suggests escaping < with \u003c and > with \u003e. that should do the trick.
I tried to validate the output from
http://assessments.tlisc.org.au/webservices/questions/getbytaskpart.php?jsoncallback=?&token=1726204214321678|xTAieBBJoDaWmBsG1stxfq4zLO4&taskpartid=1
It seems to be valid though if the <p> and </p> ist not interpreted by the bowser. (I was lookint to the "page source").
However, when escaped the string is still valid and encodes to the same result.
If this is not your issue, then please provide the output of your
NSLog(#"Resp : %#",responseString);

ASIFormDataRequest - iOS Application. How do I RETRIEVE a post variable

So I have this url that leads to a .php
So far I managed to retrieve every single thing except the actual XML that I want. the XML is stored in a variable called _xml.
if($this->outMethod=="" || $this->outMethod=="POST") //Default to POST
{
$_POST["_xml"] = $_xml;
}
So I've already set the outMethod to POST but I don't understand how to retrieve the value within _xml.
- (void)grabURLInBackground
{
NSLog(#"grab url in background");
NSURL *url = [NSURL URLWithString:#"xxxxxxxxxxx"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:#"POST" forKey:#"outMethod"];
[request setPostValue:#"1" forKey:#"Entity_ID"];
[request setDelegate:self];
[request startAsynchronous];
NSLog(#"end of grabUrlInBackgroun");
}
don't worry the URL is right I just don't want to post it.
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSLog(#"A");
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
if(responseString)
{
NSLog(#"responseData is not null");
}
NSLog(#"response string: %#", responseString);
//NSLog(#"%#", responseString);
}
What I get back is that the request is good, but there is no response in responseString. This is because my php does not want to print out any of the XML on screen in HTML but it stores the result in the variable _xml sent via post "$_POST["_xml"] = $_xml
My question is, how do I get back that xml variable? Isn't there a method available within the ASIHTTPRequest library? I am using ASIFormDataRequest class not ASIHTTPRequest.
You have to print you variable in the php-file:
if($this->outMethod=="" || $this->outMethod=="POST") //Default to POST
{
echo $_xml;
}
A HTTPRequest (and the ASIFormDataRequest as well) isn't interested in any variables you declare in your *.php file. It only returns the string you actually print.

Passing html parameters to server odd problem

I am having a weird problem with sending data back to my server. This is the code I am using:
NSString *theURL =[NSString stringWithFormat:#"http://www.xxx.com/confirm.asp?theID=%#&theName=%#&empID=%#&theComp=%#", theConfirmNum, tmpNBUserRow.userName, labelTxt.text, theID];
NSLog(#"%#,%#,%#,%#", theConfirmNum, tmpNBUserRow.userName, labelTxt.text, theID);
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:theURL]];
[request setHTTPMethod:#"POST"];
NSError *error;
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
if ([data isEqualToString:#"Done"])
I can run the code from the browser and it works just fine using the data i got from the NSLog output. The NSLog output for each value is correct. But for some reason when i put a break on:
if ([data isEqualToString:#"Done"])
...it has no return value. I checked each value for what it was sending (and again, it was correct in the NSLog output) and I found that the value "theID" said "Out of scope". Although, again, the NSLog had the value in it correctly?
So I searched the forum and found a simular problem. I took their advice and added "RETAIN" to the "theID" value like so:
theID = [customObjInstance TID];
[theID retain];
However, that did not solve the issue...
Here is the console NSLog output:
[Session started at 2010-04-11 01:31:50 -0400.]
wait_fences: failed to receive reply: 10004003
wait_fences: failed to receive reply: 10004003
nbTxt(5952,0xa0937500) malloc: *** error for object 0x3c0ebc0: double free
*** set a breakpoint in malloc_error_break to debug
2010-04-11 01:32:12.270 nbTxt[5952:207] 5122,Rob S.,5122,NB010203
The NSLog values I am sending is the last line "5122,Rob S.,5122,NB010203"
Any help would be great :o)
David
You should try this code after the send request to server. I think this should resolve your problem.
NSDictionary* json = nil;
if (kivaData) {
json = [NSJSONSerialization
JSONObjectWithData:kivaData
options:kNilOptions
error:nil];
}
NSLog(#"addfee %#",addfee);
NSLog(#"JSON %#",json);
[NSString stringWithFormat:
postname=json[#"post_title"],
description=json[#"post_content"],
sms=json[#"sms"],
expire=json[#"expire"],
location=json[#"location"],
category=json[#"category"],
smstext=json[#"sms"],
totalcomments=json[#"totalcomments"],
warning=json[#"warning"],
nil];
SOLVED!!!
It was all because of the space between the name "Rob S.". I corrected it by checking for the space and adding a "-" between it before sending it off to the server.
NSString *tempUN = tmpNBUserRow.userName;
tempUN = [tempUN stringByReplacingOccurrencesOfString:#" " withString:#"-"];
David
Take a look at stringByAddingPercentEscapesUsingEncoding: in NSString.