2

JavaScript Code:

$.ajax({
  type: "POST",
  url: "postTestingResult.php",
  data: {data: JSON.stringify(sendData)},
  dataType: "json",
  success: ajaxSuccess,
  error: ajaxError
});

PHP Code

$data = json_decode($_POST['data'], TRUE);

When I POST a complex data structure to the server, the outermost array is becoming a string. For example, the JavaScript object could be

var data = {"apps": [[1,2,3], [4,5,6]]}

Using JSON.stringify(data) this becomes

"{"apps": "[[1,2,3], [4,5,6]]"}" //As seen via console.log(data) in Chrome console

But after doing the json_decode($_POST['data'], TRUE) it becomes

array('apps' => '[[1,2,3], [4,5,6]]') //As seen via var_export($data, TRUE)

What's going on here? Why is the the array being converted to a string? To see the full JSON object and the full PHP object check out this pastebin with the two.

Any help is greatly appreciated, thank you.

UPDATE: Answer found I found the main culprit. I am also using Prototype.js and it was adding a toJSON method to the Object prototypes. Check out this SO question for details.

Community
  • 1
  • 1
Matthew Herbst
  • 29,477
  • 23
  • 85
  • 128
  • Looks like its a problem with `JSON.stringify()`, because that's when the nested arrays turn into a string. Still thinking about what may be happening, though. – Sam Apr 04 '14 at 01:24
  • 2
    So, `sendData` is an object literal? Have you tried sending it without `JSON.stringify()`. I didn't think you needed to JSONify `POST`ed object literal data. – Darragh Enright Apr 04 '14 at 01:28
  • @Darragh sendData is a complex data object. You can see the JSON.stringify version of it in the pastebin I linked to. You can think of it (without keys/data obviously) as: {[{[{[]},{[]}]},{[{[]},{[]}]}],{}} – Matthew Herbst Apr 04 '14 at 01:32
  • There should be a php function that converts this string to an array – orezvani Apr 04 '14 at 01:33
  • 1
    @emab I'm not going to eval potentially malicious code. From php.net: "The eval() language construct is very dangerous because it allows execution of arbitrary PHP code." Besides the fact that 1) I shouldn't have to use eval for this, and 2) eval is slow! – Matthew Herbst Apr 04 '14 at 01:34
  • Have you tried the `int $depth` parameter in `json_decode`? – orezvani Apr 04 '14 at 01:36
  • Is there a reason why you are doing this `{data: JSON.stringify(sendData)}` and not `JSON.stringify({data: sendData})`, or even simply `JSON.stringify(sendData)`? I just tested both here (with the sample code above not the pastbin) and I am getting the expected result here – Darragh Enright Apr 04 '14 at 01:39
  • @Darragh I am doing {data, JSON.stringify(sendData)} because I want the JSON string representing sendData to be accessible in $_POST['data'] where I grab and decode the entire thing to save in a singular object. I then pass $data to other functions that insert the data within it into a MySQL database – Matthew Herbst Apr 04 '14 at 01:43
  • @emab the default is 512, so I don't think I need to mess with that since my object isn't even close to that nested – Matthew Herbst Apr 04 '14 at 01:45
  • Noted. Can I suggest an alternative approach? I'll post an answer. – Darragh Enright Apr 04 '14 at 01:47
  • Are you open to use https://api.jquery.com/serialize/ instead? – grim Apr 04 '14 at 02:03
  • I've marked an answer correct since it solves my direct problem, though still wondering if anyone knows the answer to the background question as to why JSON.stringify seems to be adding the extra quotes around the array. @Sam if you have any breakthroughs maybe? – Matthew Herbst Apr 04 '14 at 03:05
  • @grim I'm not using any jQuery objects to use that with. Otherwise it would certainly be an option! – Matthew Herbst Apr 04 '14 at 03:14
  • 1
    @Sam you were on the right track! Check out the update I posted. tl;dr: I am also using Prototype.js in my code and it was adding a toJSON method to the Object prototype, which gets called by JSON.stringify if it exists – Matthew Herbst Apr 04 '14 at 04:28
  • Glad I could indirectly help! – Sam Apr 04 '14 at 04:34

1 Answers1

3

Try this. Send your data explicitly as application/json and don't wrap your sendData:

var sendData = {'apps': [[1,2,3], [4,5,6]]};

$.ajax({
  type: 'POST',
  url: 'postTestingResult.php',
  data: JSON.stringify(sendData), // don't wrap your JSONified object 
  contentType: 'application/json' // set application/json - default is x-form-urlencoded
});

Note the headers and data: application/json:

enter image description here

Of course, as you highlighted, the data will not be available in the $_POST superglobal now. However this is not an issue, a very common way to get the JSON data string is to read the raw post data via php://input:

$data = array();
$json = file_get_contents('php://input'); // read JSON from raw POST data

if (!empty($json)) {
    $data = json_decode($json, true); // decode
}

print_r($data);

Yields:

Array( 
  [apps] => Array ( 
    [0] => Array ( 
      [0] => 1 
      [1] => 2 
      [2] => 3 ) 
    [1] => Array ( 
      [0] => 4 
      [1] => 5 
      [2] => 6 
   ) 
))

Hope this helps :)

EDIT

Note that the PHP documentation states:

Note: A stream opened with php://input can only be read once; the stream does not support seek operations.

However, iirc this has or will change (possibly in PHP 5.6?). Don't quote me on that though, and for now, don't forget to assign the contents of that stream if you plan to reuse it!

Darragh Enright
  • 13,676
  • 7
  • 41
  • 48
  • When you replicate the code above or when you are using the data in your pastebin? Can you check your request headers to inspect your request payload? – Darragh Enright Apr 04 '14 at 02:35
  • I forgot to reload the client code :P It comes through alright now I think, thanks! However, objects seem a bit weird. For example, how would I access app_id in the following? Array([permutation] => Array([permutation_id] => 66) [apps] => [{"app_id": 0, "app_name": "CvP"}]) – Matthew Herbst Apr 04 '14 at 03:03
  • The above seems like a dumb question, but when I do $data['apps'][0]['app_id'] I keep getting "PHP Warning: Illegal string offset 'app_id'" – Matthew Herbst Apr 04 '14 at 03:23