24

There appears to be an oddity with json_encode and/or json_decode when attempting decode a string that was produced by json_encode:

    $object = new stdClass;
    $object->namespace = 'myCompany\package\subpackage';

    $jsonEncodedString = json_encode($object);
    echo $jsonEncodedString;
    // Result of echo being:
    // {"namespace":"myCompany\\package\\subpackage"}

    $anotherObject = json_decode($jsonEncodedString);
    echo $anotherObject->namespace;
    // Result of echo being:
    // myCompany\package\subpackage


    $yetAnotherObject = json_decode('{"namespace":"myCompany\\package\\subpackage"}');
    // Result should be:
    // myCompany\package\subpackage
    // But returns an invalid string sequence error instead...
    echo json_last_error_msg();

I've never experienced a problem previous to this, as I've never had to capture a string with backslashes in it until today. In reviewing the code above, I can encode/decode utilizing PHP's built in components. However if I attempt to decode a string that was produced by this encoder I get an error message. I've read in documentation items like "Predefined Constants" and other stack questions like "how remove the backslash (“\”) in the json response using php?". But I can't seem to find a reason WHY I can't decode a string that was produced by json_encode. The PHP version I'm using to test this code is 5.5.9.

I know I could be totally clueless by missing something, but should I be handling my string that was produced by json_encode differently if I attempt to use it elsewhere?

Community
  • 1
  • 1
J. Gavin Ray
  • 324
  • 1
  • 2
  • 12

6 Answers6

26

The answer is in the question:

$jsonEncodedString = json_encode($object);
echo $jsonEncodedString;
// Result of echo being:
// {"namespace":"myCompany\\package\\subpackage"}

You don't have to strip any slashes. On the contrary. You have to express the echo-ed text as PHP source code.

$yetAnotherObject = json_decode('{"namespace":"myCompany\\\\package\\\\subpackage"}');

The backslash (\) is a special character in both PHP and JSON. Both languages use it to escape special characters in strings and in order to represent a backslash correctly in strings you have to prepend another backslash to it, both in PHP and JSON.

Let's try to do the job of the PHP parser on the string above. After the open parenthesis it encounters a string enclosed in single quotes. There are two special characters that needs escaping in single quote strings: the apostrophe (') and the backslash (\). While the apostrophe always needs escaping, the PHP interpreter is forgiving and allows unescaped backslashes as long as they do not create confusion. However, the correct representation of the backslash inside single quoted strings is \\.

The string passed to function json_decode() is

{"namespace":"myCompany\\package\\subpackage"}

Please note that this is the exact list of characters processed on runtime and not a string represented in PHP or any other language. The languages have special rules for representing special characters. This is just plain text.

The text above is interpreted by function json_decode() that expects it to be a piece of JSON. JSON is a small language, it has special rules for encoding of special characters in strings. The backslash is one of these characters and when it appears in a string it must be prepended by another backslash. JSON is not forgiving; when it encounters a backslash it always treats it as an escape character for the next character in the string.

The object created by json_decode() after successful parsing of the JSON representation you pass them contains a single property named namespace whose value is:

myCompany\package\subpackage

Note again that this is the actual string of characters and not a representation in any language.

What went wrong?

Back to your code:

$yetAnotherObject = json_decode('{"namespace":"myCompany\\package\\subpackage"}');

The PHP parser interprets the code above using the PHP rules. It understands that the json_decode() function must be invoked with the text {"namespace":"myCompany\package\subpackage"} as argument.

json_decode() uses its rules and tries to interpret the text above as a piece of JSON representation. The quote (") before myCompany tells it that "myCompany\package\subpackage" must be parsed as string. The backslash before package is interpreted as an escape character for p but \p is not a valid escape sequence for strings in JSON. This is why it refuses to continue and returns NULL.

Community
  • 1
  • 1
axiac
  • 68,258
  • 9
  • 99
  • 134
  • 1
    what if I want to use the `json_encode` from PHP directly in javascript, like `JSON.parse('$encoded')` then it will not work. – shamaseen May 18 '20 at 22:17
  • The string generated by [`json_encode()`](https://www.php.net/manual/en/function.json-encode) is a perfectly valid JavaScript data definition. A fragment (of HTML+PHP) like this: `let x = ;` generates a correct fragment of JavaScript. There isn't anything to decode. – axiac May 18 '20 at 22:23
  • if you insist on it, take this JSON that is generated by json_encode of an entity in the database. `{"id":20,"user_id":83,"done_at":null,"created_at":"2020-05-18 23:26:21","updated_at":"2020-05-18 23:26:21","taskable_type":"App\\Entities\\Tasks\\NormalTask","taskable_id":1,"counter":0}` try to parse that using javascript, it will not work because it has a namespace that `json_encode` has escaped, but JSON is not seeing it as escaped! – shamaseen May 18 '20 at 22:33
  • 1
    It is [perfectly valid JSON](https://jsonformatter.org/62a11c) and it can be used directly as [JavaScript code](https://repl.it/repls/DarkvioletLovelyInterface). Do not wrap a JSON in quotes or apostrophes! It does not make it a valid JavaScript string. Use it as is. If you want to parse it using `JSON.parse()` then write it into a file then read it from there. – axiac May 18 '20 at 22:43
  • that was awesome, it works for me! but I was thinking that I always need to parse data from PHP to convert it to JS object, can you please illustrate more, if no need to parse the data then what is the use of `JSON.parse`? – shamaseen May 18 '20 at 22:54
  • JSON is text. It can be stored in files (many programs use it nowadays to store their configuration), in the database or it can be downloaded from another server. In such cases it needs to be parsed (using `JSON.parse()`, f.e.) to get the original data back. But JSON is also a valid JavaScript fragment. You can use it as is to produce a larger script (the JSON provides only the representation of a data structure as JavaScript code). In this case the JavaScript compiler does the parsing, there is no need to parse it explicitly. – axiac May 18 '20 at 23:00
4

You will need to encode your double backslashes \\ into quadruple backslashes \\\\ because php interprets a backslash as an escape character in single and double quoted strings. This means that \\ is seen as \.

This example illustrates what is happening

<?php
$str = '{"namespace":"myCompany\\package\\subpackage"}';
echo $str, PHP_EOL; // \\ seen as \
$yetAnotherObject = json_decode($str);
echo json_last_error_msg(), PHP_EOL;

var_dump(json_decode(str_replace('\\', '\\\\', $str)));
echo json_last_error_msg(), PHP_EOL;

This is the output

$ php test.php
{"namespace":"myCompany\package\subpackage"}
Syntax error
class stdClass#1 (1) {
  public $namespace =>
  string(28) "myCompany\\package\\subpackage"
}
No error
Josh J
  • 6,813
  • 3
  • 25
  • 47
0

Please use the PHP native function stripslashes

https://www.php.net/manual/en/function.stripslashes.php

<?php
$str = 'myCompany\\package\\subpackage';
echo stripslashes(json_encode($str));
//output: myCompany\package\subpackage
soduncu
  • 51
  • 1
  • 9
  • Not relevant to OP's question. If you `stripslashes` from json_encoded string, the JSON representation won't match the originally encoded value – Kita Oct 11 '22 at 20:38
-1

Thats because PHP is converting the double slashes into a single slash before passing it to json_decode. You will need to either escape the double slashes (a pain I know) or construct the object manually.

http://php.net/manual/en/language.types.string.php#language.types.string.syntax.single

Blake A. Nichols
  • 870
  • 1
  • 6
  • 11
  • So to be clear, the suggestion is to do something like: a str_replace() on the string and add an additional backslash to the string that I am attempting to json_decode? Seems kinda janky. – J. Gavin Ray Aug 17 '15 at 18:23
  • Yeah, its a weird workaround, buts its the easiest way to do it. str_replace( "\\", "\\\\", $input ); – Blake A. Nichols Aug 17 '15 at 18:25
  • This really seems odd, because now after I sanitize my input I now have to check each and every string for backslashes and do a replace. It seems like this shouldn't be something that should be handled at the programming level but at the language level. Well I learned something today, thanks Blake! – J. Gavin Ray Aug 17 '15 at 18:42
  • PHP doesn't convert anything. You mistake the conventions used by a language to represent special characters in a string with the sequence of characters produced by the parser on the runtime by following the conventions. `\\` is the way to tell the PHP parser you want a `\` in the string. – axiac Aug 17 '15 at 18:53
-1

Unfortunately, the json_decode() throws a JsonException when it is trying to decode a JSON string with double backslashes ("\\").

<?php
var_dump(
    json_decode(
        '{"Server": "\\\\ComputerName\\MainDirectory\\SubDirectory"}',
    false, 512, JSON_THROW_ON_ERROR)
);

<br />
<b>Fatal error</b>:  Uncaught JsonException: Syntax error in [...][...]:2
Stack trace:
#0 [...][...](2): json_decode('{&quot;Server&quot;: &quot;\\\\C...', false, 512, 4194304)
#1 {main}
  thrown in <b>[...][...]</b> on line <b>2</b><br />

To solve this problem, We could convert those double backslashes ("\\") into quad backslashes ("\\\\") using str_replace().

<?php
var_dump(
    json_decode(
        str_replace('\\','\\\\','{"Server": "\\\\ComputerName\\MainDirectory\\SubDirectory"}'),
        false, 512, JSON_THROW_ON_ERROR
    )
);


object(stdClass)#1 (1) {
    ["Server"]=>
    string(41) "\\ComputerName\MainDirectory\SubDirectory"
}

Hope this helps.

Marc
  • 11
  • 1
  • 1
    This is not correct. First of all, don't use string manipulation when working with JSONs. Second of all, the error you speak of `JsonException`, is because of invalid escape in your JSON, in this case that's `\M` and `\S`. So this input JSON is malformed to begin with. Perhaps it was incorrectly encoded or copied? – Danon Jun 24 '21 at 07:33
-2

You can just use the stripslashes function to strip the dashes and it will work tested

  $yetAnotherObject = json_decode(stripslashes('{"namespace":"myCompany\\package\\subpackage"}'));
AleMelo
  • 128
  • 4
  • 1
    If you stripslashes(), then you are changing the data within the object to : {"namespace":"myCompanypackagesubpackage"} which is a different value. – J. Gavin Ray Aug 17 '15 at 18:30
  • As from docs: "Returns a string with backslashes stripped off. (\' becomes ' and so on.) Double backslashes (\\) are made into a single backslash (\)." so in this case it would only strip one of the two slashes, which however is not the expected result. – Ion Farima Jul 15 '18 at 19:28