背景
最近在对接国内的外卖平台,代码啥的正常开发、测试、上线都没发现什么异常的。突然,最近有运维反馈说漏了单据,一开始我以为是外卖平台那边接口异常,拉单延迟导致的,就没怎么放在心上。过了半天,运维那边继续反馈此类问题,且最开始那个订单依旧没有同步,这个时候我看了接口响应都是正常的,但是JSON解析返回了NULL,导致后续流程全错了。但是测试环境验证也没办法复现这个Bug,实在是百思不得其解。一开始我拿外卖平台返回的JSON,在百度搜一个JSON在线解析网站,解析之后也是正常的。后面我无意中发现这个JSON有个地方有点猫腻,某个Unicode字符没有编码成功,显示一个黑色方框,类似这样”�“。这里我产生了一个疑问,会不会是这个玩意导致代码出现的异常?
JSON
{"name":"\ud870**"}
在线JSON解析后的结果:
分析一
将这块错误的JSON块单独在程序上解析下
PHP
<?php var_dump(json_decode('{"name":"\ud870**"}', true)); // 输出:NULL echo json_last_error(); // 输出:10 echo json_last_error_msg(); // 输出:Single unpaired UTF-16 surrogate in unicode escape
由此可见,正是这块内容导致的JSON解析错误,Single unpaired UTF-16 surrogate in unicode escape。这里基本上找到问题了,后面看看如何解决。
一开始我照着其他博主的想法,增加一个 $flag 参数进行调整,JSON_INVALID_UTF8_IGNORE | JSON_INVALID_UTF8_SUBSTITUTE,但是依旧无法正常解析
PHP
<?php var_dump(json_decode('{"name":"\ud870**"}', true, 512, JSON_INVALID_UTF8_IGNORE | JSON_INVALID_UTF8_SUBSTITUTE)); // 输出:NULL
后面就搜一下这个Unicode字符到底是个啥东西,发现这个可能是一个emoji表情包,这里我突发奇想,我直接通过字符串的形式直接把这个Unicode解析异常的字符直接改了行不行?思路很简单,我利用JSON解析的原理,如果”\uxxxx“这类Unicode字符返回了NULL,就证明这个JSON解析是异常的,我把这个字符直接替换掉,这样就可以实现JSON正常解析了,代价就行无法真正显示JSON正常的内容。
PHP
<?php /** * 替换JSON中无效的unicode数据,常见的有emoji表情包. */ function replaceInvalidJson(string $json): string|null { return preg_replace_callback( '~\\\u[a-z0-9]{4}~iu', function ($value) { if (json_decode('"' . $value[0] . '"')) { return $value[0]; } return '?'; }, $json ); } var_dump(json_decode(replaceInvalidJson('{"name":"\ud870**"}'), true)); // 输出:array(1) { ["name"]=> string(3) "?**" }
这里发现是OK的,我把异常的Unicode直接用"?"代替了,并不影响后续的流程。
分析二
这个是网上其他大神提供的方案,原理并不难,就是手动实现解析
PHP
<?php function customJsonDecode($json) { $comment = false; $out = '$x='; for ($i = 0; $i < strlen($json); $i++) { if (!$comment) { if (($json[$i] == '{') || ($json[$i] == '[')) $out .= ' array('; else if (($json[$i] == '}') || ($json[$i] == ']')) $out .= ')'; else if ($json[$i] == ':') $out .= '=>'; else $out .= $json[$i]; } else $out .= $json[$i]; if ($json[$i] == '"' && $json[($i - 1)] != "\\") $comment = !$comment; } eval($out . ';'); return $x; } var_dump(customJsonDecode('{"name":"\ud870**"}')); // 输出:array(1) { ["name"]=> string(8) "\ud870**" }
也是可以正常解析,而且还原封不动保留Unicode字符,缺点也是比较明显的,所有的Unicode都不转成中文,阅读起来压力不小。
结论
正确的JSON有可能由于编码问题,部分编程语言解析会报错,这个时候需要根据实际情况进行取舍。
标签:字符,PHP,json,JSON,Unicode,解析,知道 From: https://www.cnblogs.com/lyc94620/p/17060605.html