I was trying to save some data (a table) as JSON in my database with AngularJS and PHP. For that, I would want to convert it to string first and then escape special characters (avoiding SQL injection). Once it's saved, I can retrieve it as a string from DB, convert it back to JSON and display in HTML. To simplify it, let's ignore the database and just return the JSON.
The problem occurred with conversions. Just sending string version of JSON, receiving it as is, and parsing it back from string would result in a string. E.g.
var data = {
"json": JSON.stringify({"A": "B"}) // some random JSON
};
$http.post("php/sendJSON.php", data). // AJAX post request
then(function(res) {
console.log( res.data ); // response from back-end
console.log( JSON.parse(res.data) ); // parsing it to an object
});
and my back-end:
<?php
$postdata = file_get_contents("php://input");
$res = json_decode($postdata);
$json = $res->json;
echo json_encode($json);
?>
My both logs printed ""{\"A\":\"B\"}""
and "{"A":"B"}"
. So the parsed response had double quotes and JSON.parse
didn't turn it into an object. A hack would be to use JSON.parse(JSON.parse(res.data))
, but I was wondering if there is a proper way. (Same thing for angular.fromJson(res.data)
)
Beyond that issue I was interested in escaping the string. (I used mysqli::real_escape_string
) But that would add more complexety to parsing. E.g. this time it will be ""{\\\"A\\\":\\\"B\\\"}""
and "{\"A\":\"B\"}"
in logs. So I tried to manually unescape it with:
function unEscapeJsonString($value) {
$escapers = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b");
$replacements = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c");
$result = str_replace($escapers, $replacements, $value);
return $result;
}
This finally gave me an object, but once again it felt like a hack, and unescaping still doesn't solve the problem with a double JSON.parse
that I needed to use.
The biggest issue with this is when I try to have new lines and white spaces in JSON, {"A":"B \n C"}
, it will not parse it twice and always return a string. Another hack for this is to use:
JSON.parse(res.data).replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
So the question is: What is the correct way of sending JSON as a string, escaping it, and parsing it back to JSON? (Requests: AngularJS -> PHP -> AngularJS)
var data = { "json": {"A": "B"} };
? Or justvar data = {"A": "B"}
(and on the server side then just$postdata = file_get_contents("php://input"); $res = json_decode($postdata); echo json_encode($res);
to do the back and forth)? I'm not really sure why you're stringifying it at all, or wrapping it inside another object. What's the reason for that? I can't see what value it creates to add those layers of indirection. Just seems to create problems.$http.post(url, {"A":"B"})
works, it let's me to parse it correctly. But I need to sent more data, and therefore this simple example will not work. That's why it's JSON with stringified JSON inside it. The reason why it's a string is so that I can save it in my database (MySQL 5.5) as some TEXT (medium / var).var data = { "field1": {"A": "B"}, "field2": "XYZ", "field3" [1, 2, 3] }
, hopefully you get the idea{"json":{"A":"B"}, "other_data":[...]}
(where "json" property is only turned into a string). But either way, if I turn it into a string withJSON.stringify()
orjson_encode()
, I still have to (because it's AngularJS) return it from back-end withecho json_encode($json);
, which treats it as an unnecessary, second encoding