PHP解码和编码具有Unicode字符的JSON

48
我有一些JSON需要解码、修改,然后重新编码,不能弄乱任何字符。如果JSON字符串中有一个Unicode字符,它将无法解码。我不知道为什么,因为json.org说一个字符串可以包含:除“-”、“\”或控制字符之外的任何Unicode字符。但在Python中也不起作用。
{"Tag":"Odómetro"}

我可以使用 utf8_encode,这将允许字符串使用 json_decode 解码,但是该字符会变成其他字符。以下是 print_r 结果数组的输出。两个字符。

[Tag] => Odómetro

当我再次对数组进行编码时,字符被转义为ascii码,这符合json规范:

"Tag"=>"Od\u00f3metro"

有没有方法可以将其还原?json_encode没有这样的选项,utf8_encode似乎也不起作用。

编辑我看到json_encode有一个unescaped_unicode选项。但是它并没有按预期工作。天啊,这只在php 5.4上才有。我必须使用一些正则表达式,因为我只有5.3。

$json = json_encode($array, JSON_UNESCAPED_UNICODE);
Warning: json_encode() expects parameter 2 to be long, string ...

你的输入使用哪种字符集?UTF-8?还是其他的? - John Flatness
11
JSON_UNESCAPED_UNICODE 是 PHP 5.4 中新增的(也就是说,它还不存在)。 - John Flatness
1
如果您已经在使用UTF-8,那么您绝对不想使用utf8_encode,因为它是设计用于将ISO 8859-1转换为UTF-8。这个字符串来自数据库、字符串字面量还是其他来源?(提出所有这些问题的原因是:json_encode专门构建为仅与UTF-8字符串一起使用)。 - John Flatness
它来自一个设置编码为UTF-8的Postgres数据库。我不确定为什么它不能被解析,即使Python也无法解析它。 - Benbob
8个回答

64

我已经找到了解决这个问题的方法...希望这能帮助你。

json_encode($data,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);

1
好的回答。斜杠和UNICODE是JSON编码中的两个主要问题。如果你处理好了这两个问题,它将在90%的情况下完美运行。 - Harikrishnan
1
@Harikrishnan,你的90%基于什么? - Skoempie
从我的经验来看,这个百分比会因不同的环境而发生变化。 - Harikrishnan
这是正确的方法。在我的情况下,使用这些标志显著减小了生成文件的大小; - Vahid Amiri
这是一个完美的答案。@sunny sm - shawn
显示剩余2条评论

18
从你说的一切来看,你处理的原始 Odómetro 字符串是使用 ISO 8859-1 编码而不是 UTF-8。
以下是我为什么这样认为的:
- 在你将输入字符串通过 utf8_encode 转换成 UTF-8 后,json_encode 生成可解析的输出。 - 当你使用 print_r 进行 utf8_encode 之后,你确实说获取了 "乱码" 输出,但你得到的这个乱码实际上正是尝试将 UTF-8 文本解析为 ISO 8859-1 所产生的结果("ó" 在 UTF-8 中是 \x63\xb3,但在 ISO 8859-1 中这个序列是 ó)。 - 你的 htmlentities 解决方案起作用了。为了正确运行,htmlentities 需要知道输入字符串的编码。如果你不指定编码,它会假定为 ISO 8859-1。(令人困惑的是,默认情况下,html_entity_decode 使用的编码是 UTF-8,所以你的方法实际上是从 ISO 8859-1 转换成了 UTF-8。) - 你说在 Python 中有同样的问题,这似乎排除了 PHP 是问题的原因。 - PHP 将使用 \uXXXX 转义,但正如你所指出的,这是有效的 JSON。
因此,看起来你需要配置连接到 Postgres,以便它可以提供给你 UTF-8 字符串。PHP 手册表明你可以在连接字符串中添加 options='--client_encoding=UTF8' 来实现此目的。数据库中当前存储的数据编码可能也有问题。(你可以简单地使用 utf8_encode,但这只支持 ISO 8859-1 的字符。)

最后,正如另一个答案所指出的那样,您确实需要确保声明正确的字符集,使用HTTP标头或其他方式(当然,这个特定问题可能只是在您进行 print_r 测试的环境中产生的结果)。


感谢您详细的回答。由于即使使用htmlentities($item, NULL, 'UTF-8'),日语字符也会被转换为\uXXXX,因此似乎是正确的。因此,我只能假设输入字符串不是utf-8编码。目前我只是在测试一个简单的表单,但似乎pg库使用ISO字符串编码。 - Benbob
遇到了类似的问题。echo 打印出正确的字符,但 json_encode 把 诶ó 转换为 \u8bf6\u00f3 - Muhammad Babar

16

JSON_UNESCAPED_UNICODE 是在 PHP 5.4 中添加的,所以看起来您需要升级您的PHP版本以利用它。不过5.4还没有发布! :(

但是如果您想在开发机上尝试,可以在QA上找到5.4 alpha版本候选版


不幸的是,我被困在5.3上,但我在下面的答案中找到了一个解决方法。 - Benbob

8
在PHP 5.3中实现JSON_UNESCAPED_UNICODE的一个hack方式。对于PHP的JSON支持感到非常失望。也许这可以帮助其他人。
$array = some_json();
// Encode all string children in the array to html entities.
array_walk_recursive($array, function(&$item, $key) {
    if(is_string($item)) {
        $item = htmlentities($item);
    }
});
$json = json_encode($array);

// Decode the html entities and end up with unicode again.
$json = html_entity_decode($rson);

5
如果你传递给$array的字符串使用ISO 8859-1编码,这种方法只有在此情况下才能可靠地工作(我不想继续重申同样的观点)。实际上,这是一种将ISO 8859-1转换为UTF-8的复杂方法。这将使得生成的JSON没有Unicode转义序列,但如果你的输入字符串是UTF-8,则必须将字符集参数设置为'UTF-8'才能使它起作用。 - John Flatness

7
$json = array('tag' => 'Odómetro'); // Original array
$json = json_encode($json); // {"Tag":"Od\u00f3metro"}
$json = json_decode($json); // Od\u00f3metro becomes  Odómetro
echo $json->{'tag'}; // Odómetro
echo utf8_decode($json->{'tag'}); // Odómetro

你很接近了,只需要使用utf8_decode。


SO是一个仅限英语的网站。 - user3559349
一旦翻译完成,似乎还不错:)。甚至有字符和源代码的进展! - Félix Adriyel Gagnon-Grenier

4
尝试在你的页面中设置 utf-8 编码:
header('content-type:text/html;charset=utf-8');

这对我有效:

$arr = array('tag' => 'Odómetro');
$encoded = json_encode($arr);
$decoded = json_decode($encoded);
echo $decoded->{'tag'};

好的提示,但这只解决了一半的问题。我需要正确地对其进行编码。这甚至不会被显示出来,它纯粹是用于数据操作。 - Benbob

4

尝试使用:

utf8_decode() and utf8_encode

1
要编码包含特殊字符的数组,需将ISO 8859-1转换为UTF8。(如果utf8_encode和utf8_decode对您不起作用,这可能是一种选择)
所有在ISO-8859-1中的内容都应该转换为UTF8:
$utf8 = utf8_encode('이 감사의 마음을 전합니다!'); //contains UTF8 & ISO 8859-1 characters;    
$iso88591 = mb_convert_encoding($utf8, 'ISO-8859-1', 'UTF-8');
$data = $iso88591;

在此之后应该可以正常使用编码:

$encoded_data = json_encode($data);

将UTF-8和ISO 8859-1相互转换


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接