PHP 在进行 json 解析的时候会有很多报错,如果解析失败,可以使用json_last_error_msg
方法,它会返回一个解析失败的原因。原因之一就有一个JSON_ERROR_CTRL_CHAR
,表示Control character error, possibly incorrectly encoded
,提示有可能 JSON 对象中包含了控制字符串。下面是一个例子:
{ "hello":
" world"
}
看不出来,可以复制这段文本,使用 vim 打开,然后设置显示隐藏字符,可以在命令模式下,输入:set list
,可以看到如下的字符, ^I
表示制表字符,$
表示换行符
^I{^I"hello":^I$
"^Iworld"$
}$
什么是控制字符
可以通过这个 ASCII CODE 看一下 control character 都包含哪些字符,很明显制表符和换行符都属于 control character。
JSON 规范
那上边的 JSON 中开头和字符串中都包含控制字符,到底是哪个导致的。需要我们看看 JSON 的规范。RFC8259。
开头和结尾
其中对 JSON 做了明确的规范,表明开头和结尾都可以包含空白字符,所以我们把开头的制表符和结尾的换行符去掉后仍然无法解析或者说他们并不影响我们的解析。
字符串
规范还对字符串 string 做了声明
A string begins and ends with quotation marks. All Unicode characters may be placed within the quotation marks, except for the characters that MUST be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F).
可以看到,所有的字符必需被引号包裹,并且强调引号
,反斜线
, 控制字符
需要被转义,这也是导致我们无法解析的原因。
解决办法
既然是因为字符串中包含了控制字符,我们可以根据规范中提到的控制字符范围,把这些字符替换掉。如果我们把它们转义那么开头的字符就会被转义成\t
,但是规范要求开头必需是空白字符,所以只能转义字符串内部的控制字符。如果可以不需要控制字符的可以直接替换成空字符:
$json_string = preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', $json_string);