我正在使用MariaDB的COLUMN_JSON()
功能。正如this bug所示,该函数正确地转义了双引号,但没有其他应该编码/转义的字符。
这是一个愚蠢的示例查询,用于演示如何创建JSON列。
SELECT CONCAT('[', GROUP_CONCAT(COLUMN_JSON(COLUMN_CREATE(
'name', `name`,
'value', `value`
)) SEPARATOR ','), ']') AS `json`
FROM `settings`
如果name
或value
包含无效的JSON字符,则json_decode
将失败。
我编写了一个PHP函数来转义/编码来自查询的值,但似乎应该有更好的方法。
/**
* Makes sure the JSON values built by COLUMN_JSON() in MariaDB are safe for json_decode()
* Assumes that double quotes are already escaped
*
* @param string $mysql_json
* @return string
*/
public static function jsonEscape($mysql_json)
{
$rtn = '';
for ($i = 0; $i < strlen($mysql_json); ++$i) {
$char = $mysql_json[$i];
if (($char === '\\') && ($mysql_json[$i + 1] !== '"')) {
// escape a backslash, but leave escaped double quotes intact
$rtn .= '\\\\';
} elseif (($ord = ord($char)) && ($ord < 32)) {
// hex encode control characters (below ASCII 32)
$rtn .= '\\u' . str_pad(dechex($ord), 4, '0', STR_PAD_LEFT);
} else {
$rtn .= $char;
}
}
return $rtn;
}
检查字符串逐字符,但效果不佳。也许有一个字符串替换或正则表达式会更高效?
答案 0 :(得分:0)
根据Halcyon的评论,我切换到str_replace()
解决方案,效果更好! trim(json_encode(13), '"')
和'\\u' . str_pad(dechex(13), 4, '0', STR_PAD_LEFT)
之间的效果差异只是好一点,但它使意图更加清晰。
private static $json_replace_search;
private static $json_replace_replace;
/**
* Makes sure the JSON values built by GROUP_CONCAT() and COLUMN_JSON() in MariaDB are safe for json_decode()
* Assumes that double quotes are already escaped
*
* @param string $mysql_json
* @return string
*/
public static function jsonEscape($mysql_json)
{
if (is_null(self::$json_replace_search)) {
// initialize
self::$json_replace_search = [];
self::$json_replace_replace = [];
// set up all of the control characters (below ASCII 32)
for ($i = 0; $i < 32; ++$i) {
self::$json_replace_search[$i] = chr($i);
self::$json_replace_replace[$i] = trim(json_encode(self::$json_replace_search[$i]), '"');
}
}
// replace them
return str_replace(self::$json_replace_search, self::$json_replace_replace, $mysql_json);
}
/**
*
* @param string $mysql_json
* @return mixed
*/
public static function jsonDecode($mysql_json)
{
return json_decode(self::jsonEscape($mysql_json));
}