Why would json_encode return an empty string
PhpJsonPhp Problem Overview
I have a simple php structure with 3 nested arrays.
I do not use particular objects and I build myself the arrays with 2 nested loops.
Here is a sample of the var_dump of the array I want to convert to Json.
array (size=2)
'tram B' =>
array (size=2)
0 =>
array (size=3)
'name' => string 'Ile Verte' (length=9)
'distance' => int 298
'stationID' => int 762
1 =>
array (size=3)
'name' => string 'La Tronche Hôpital' (length=18)
'distance' => int 425
'stationID' => int 771
16 =>
array (size=4)
0 =>
array (size=3)
'name' => string 'Bastille' (length=8)
'distance' => int 531
'stationID' => int 397
1 =>
array (size=3)
'name' => string 'Xavier Jouvin' (length=13)
'distance' => int 589
'stationID' => int 438
In another script I have a similar structure and json_encode
works fine.
So I don't understand why json_encode
won't work here.
Edit : there seems to be a problem with the encoding. When mb_detect_encoding
returns ASCII, the json_encode
works but when it returns UTF8, it doesn't work anymore.
Edit2 : json_last_error()
returns JSON_ERROR_UTF8
which means : Malformed UTF-8 characters, possibly incorrectly encoded.
Php Solutions
Solution 1 - Php
Well after 2 hours of digging (cf Edits)
I found out following :
- In my case it's a encoding problem
mb_detect_encoding
returns probably a faulty response, some strings were probably not UTF-8- using
utf8_encode()
on those string solved my problem, but see note below
Here is a recursive function that can force convert to UTF-8 all the strings contained in an array:
function utf8ize($d) {
if (is_array($d)) {
foreach ($d as $k => $v) {
$d[$k] = utf8ize($v);
}
} else if (is_string ($d)) {
return utf8_encode($d);
}
return $d;
}
Use it simply like this:
echo json_encode(utf8ize($data));
Note: utf8_encode() encodes ISO-8859-1 string to UTF-8 as per the docs so if you are unsure of the input encoding iconv() or mb_convert_encoding() may be better options as noted in comments and other solutions.
Solution 2 - Php
Matthieu Riegler presented really good solution however I had to slightly modify it to handle objects too:
function utf8ize($d) {
if (is_array($d))
foreach ($d as $k => $v)
$d[$k] = utf8ize($v);
else if(is_object($d))
foreach ($d as $k => $v)
$d->$k = utf8ize($v);
else
return utf8_encode($d);
return $d;
}
One more note: [json_last_error()][1] may be helpful in debugging json_encode()/json_encode() functions.
[1]: http://php.net/manual/en/function.json-last-error.php "json_last_error()"
Solution 3 - Php
For me, the answer to this problem was setting charset=utf8
in my PDO connection.
$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);
Solution 4 - Php
Adam Bubela also presented really good solution who helped me solved my problem, and here is the simplified function :
function utf8ize($d)
{
if (is_array($d) || is_object($d))
foreach ($d as &$v) $v = utf8ize($v);
else
return utf8_encode($d);
return $d;
}
Solution 5 - Php
I have exactly the same problem on PHP 5.6. I use Open Server + Nginx on Windows 7. All charsets are set to UTF-8. In theory, according the official documentation, flag
> JSON_UNESCAPED_UNICODE
should solve this. Unfortunately this is not my case. I do not know, why. All snippets above do not solve my problem, thus I have found my own implementation. I believe it could help someone. At least, Russian letters pass the test.
function utf8ize($d) {
if (is_array($d) || is_object($d)) {
foreach ($d as &$v) $v = utf8ize($v);
} else {
$enc = mb_detect_encoding($d);
$value = iconv($enc, 'UTF-8', $d);
return $value;
}
return $d;
}
Solution 6 - Php
This accepted answer works. But in case you are getting your data from MySQL (as I was) there is an easier way.
Once you open you database, before you query you can set the character set using mysqli as follows:
/* change character set to utf8 | Procedural*/
if (!mysqli_set_charset($link, "utf8")) {
printf("Error loading character set utf8: %s\n", mysqli_error($link));
exit();
}
OR
/* change character set to utf8 | Object Oriented*/
if (!$mysqli->set_charset("utf8")) {
printf("Error loading character set utf8: %s\n", $mysqli->error);
exit();
}
Solution 7 - Php
I ran into this issue on a server running an older version of PHP (5.2). I was using the JSON_FORCE_OBJECT flag, and apparently that isn't supported until 5.3
So if you're using that flag be sure to check your version!
A workaround appears to be just casting to an object before encoding, like:
json_encode((object)$myvar);
Solution 8 - Php
I was getting data from ob_get_clean() and had the same problem, but above solutions don't work for me. In my case solution was this, maybe it will help somebody.
$var = mb_convert_encoding($var, 'UTF-8');
Solution 9 - Php
The return of mb_detect_encoding
may not be correct:
$data = iconv('UTF-8', 'ISO-8859-1', 'La Tronche Hôpital');
var_dump(
mb_detect_encoding($data),
mb_detect_encoding($data, array('ISO-8859-1', 'UTF-8'))
);
Depending on the default detection order, the above may return different results, so the encoding is falsely being reported as UTF-8. (Here's a larger example.)
It is likely that your data is not encoded as UTF-8, so json_encode
is returning false
. You should look at converting your strings to UTF-8 before JSON-encoding:
$fromEncoding = 'ISO-8859-1'; // This depends on the data
array_walk_recursive($array, function (&$value, $key, $fromEncoding) {
if (is_string($value)) {
$value = iconv($fromEncoding, 'UTF-8', $value);
}
}, $fromEncoding);
Solution 10 - Php
I have improved Adam Bubela's answer. I just hate it when blocks are not closed by { and }. It's cleaner and you don't introduce bugs or maybe it's that I did use Perl in the past :)
<?php
class App_Updater_String_Util {
/**
* Usage: App_Updater_String_Util::utf8_encode( $data );
*
* @param mixed $d
* @return mixed
* @see http://stackoverflow.com/questions/19361282/why-would-json-encode-returns-an-empty-string
*/
public static function utf8_encode($d) {
if (is_array($d)) {
foreach ($d as $k => $v) {
$d[$k] = self::utf8_encode($v);
}
} elseif (is_object($d)) {
foreach ($d as $k => $v) {
$d->$k = self::utf8_encode($v);
}
} elseif (is_scalar($d)) {
$d = utf8_encode($d);
}
return $d;
}
}
?>
Solution 11 - Php
using utf8_encode() on those string solved my problem.
Solution 12 - Php
If you get this data from a database, use mysqli_set_charset($connection, "utf8");
in connection when get the params from database
Solution 13 - Php
Since the output is going to end up as JSON (string) anyway, here is a function that can handle all kind of variable type:
function utf8ize($arg)
{
if (is_array($arg))
foreach ($arg as $k => $v)
$arg[$k] = utf8ize($v);
else if(is_object($arg))
return utf8ize((array) $arg);
else
return utf8_encode(strval($arg));
return $arg;
}
This answer is improvement from Matthieu Riegler and Adam Bubela's answer, but this one can handle all type of variable / data type (e.g.: resource like file handle/stream/phpgd/sql connection)
I'm using cast as array when the argument is object in order to prevent error if the object property is read only or private/protected.
Also, on else
case, I strval
it to handle other data type like resource
I'm using this function to 'force' a variable into string (e.g.: when var_dump-ing or logging to text file)