views:

4310

answers:

7

Is there any reasons why PHP's json_encode function does not escape all JSON control characters in a string?

For example let's take a string which spans two rows and has control characters (\r \n " / \) in it:

<?php
$s = <<<END
First row.
Second row w/ "double quotes" and backslash: \.
END;

$s = json_encode($s);
echo $s;
// Will output: "First row.\r\nSecond row w\/ \"double quotes\" and backslash: \\."
?>

Note that carriage return and newline chars are unescaped. Why?

I'm using jQuery as my JS library and it's $.getJSON() function will do fine when you fully, 100% trust incoming data. Otherwise I use JSON.org's library json2.js like everybody else. But if you try to parse that encoded string it throws an error:

<script type="text/javascript">

JSON.parse(<?php echo $s ?>);  // Will throw SyntaxError 

</script>

And you can't get the data! If you remove or escape \r \n " and \ in that string then JSON.parse() will not throw error.

Is there any existing, good PHP function for escaping control characters. Simple str_replace with search and replace arrays will not work.

+1  A: 

Maybe I'm blind, but in your example they ARE escaped. What about

<script type="text/javascript">

JSON.parse("<?php echo $s ?>");  // Will throw SyntaxError 

</script>

(note different quotes)

Yossarian
You can't use double quotes there, because after echoing the string it will be: JSON.parse(""... and so on.
Gustav
+3  A: 

D'oh - you need to double-encode: JSON.parse is expecting a string of course:

<script type="text/javascript">

JSON.parse(<?php echo json_encode($s) ?>);

</script>
Greg
Doesn't escaped newline character look like this: \\n ?Right now \r and \n are not escaped.
Gustav
No, escaped newlines are "\n". An unescaped newline would be, well, a new line.
Greg
"\\n" would be an escaped backslash followed by an "n" - just try alert('foo\nbar'); and alert('foo\\nbar'); in a regular bit of code.
Greg
Not correct.var d = JSON.parse('["a\\nb"]');alert(d);Will alert:abJSON.parse('["a\nb"]') will throw an error because newline is not escaped.
Gustav
Last comment doesn't show that a and b are on diffrent rows in alert box.
Gustav
I've edited my answer. I'm used to outputting JSON as actual JSON not a string!
Greg
That double encoding does the trick! :DAlthough it's bit weird to use 2nd nested json_encode: json_encode(json_encode($s)). If anyone is interested then outcome would be: "\"First row.\\r\\nSecond row w\\\/ \\\"double quotes\\\" and backslash: \\\\.\"" Not very "beautiful" but works. I leave this question still open for a while, maybe someone has better solution. Thanks Greg for your help!
Gustav
It looks weird but it is correct - The first one says "convert this object to JSON" then the second one says "convert this JSON to a Javascript string"
Greg
A: 

Just an addition to Greg's response: the output of json_encode() is already contained in double-quotes ("), so there is no need to surround them with quotes again:

<script type="text/javascript">
    JSON.parse(<?php echo $s ?>);
</script>
Stefan Gehrig
You are right, but that doesn't change the outcome.
Gustav
A: 

I still haven't figured out any solution without str_replace..

Try this code.

$json_encoded_string = json_encode(...);
$json_encoded_string = str_replace("\r", '\r', $json_encoded_string);
$json_encoded_string = str_replace("\n", '\n', $json_encoded_string);

Hope that helps...

sp2hari
+1  A: 
$search = array("\n", "\r", "\u", "\t", "\f", "\b", "/", '"');
$replace = array("\\n", "\\r", "\\u", "\\t", "\\f", "\\b", "\/", "\"");
$encoded_string = str_replace($search, $replace, $json);

This is the correct way

JunioR
A: 

function escapeJsonString($value) { # list from www.json.org: (\b backspace, \f formfeed)
$escapers = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c"); $replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b"); $result = str_replace($escapers, $replacements, $value); return $result; }

I'm using the above which escapes a backslash (must be first in the arrays) and should deal with formfeeds and backspaces (I don't think \f and \b are supported in php).

Peter Whitefield
A: 

You don’t need JSON.parse. If $s is the plain string, you can simply do this:

<script type="text/javascript">
var s = <?php echo json_encode($s); ?>;
</script>
Gumbo