json_decode AND json_encode 长整数而不丢失数据

json_decode AND json_encode long integers without losing data

如 PHP 文档中所述,当 json_decodeing 包含长整数的数据结构时,它们将被转换为浮点数。解决方法是使用 JSON_BIGINT_AS_STRING,它将它们保留为字符串。当 json_encodeing 这样的值时,JSON_NUMERIC_CHECK 会将这些数字编码回大整数:

$json  = '{"foo":283675428357628352}';
$obj   = json_decode($json, false, JSON_BIGINT_AS_STRING);
$json2 = json_encode($obj, JSON_NUMERIC_CHECK);
var_dump($json === $json2); // true

使用此方法正确往返数据很容易出错。如果一个 属性 包含 '123',一个应该保留为字符串的数字字符串,它将被编码为一个整数。

我想从服务器获取一个对象,修改一个 属性 然后将整个数据结构放回去。我需要保留原始类型。我不想维护我正在操作的属性以外的属性。

是否有真正的解决方法? PHP 大整数不再有任何问题,但 json_decode 例程似乎已过时。

只要您的 PHP 版本实际上可以处理大整数,这意味着如果您 运行 是 PHP 的 64 位版本(在某些方面other than Windows),json_decode没问题:

$json  = '{"foo":9223372036854775807}';
$obj   = json_decode($json);
$json2 = json_encode($obj);

var_dump(PHP_INT_MAX, $obj, $json2);

int(9223372036854775807)
object(stdClass)#1 (1) {
  ["foo"]=>
  int(9223372036854775807)
}
string(27) "{"foo":9223372036854775807}"

如果您需要处理的整数值超过 PHP 的 PHP_INT_MAX,您只需 不能 在 PHP 原生中表示它们类型。因此,您无法解决这个难题;您不能使用本机类型来跟踪正确的类型,也不能替换其他类型(例如字符串而不是整数),因为在编码回 JSON.

时这是不明确的

在这种情况下,您将不得不发明自己的机制来跟踪每个 属性 的正确类型,并使用自定义 encoder/decoder 处理此类序列化。例如,您需要编写一个自定义 JSON 解码器,它可以解码为自定义 class,如 new JsonInteger('9223372036854775808'),您的自定义编码器将识别此类型并将其编码为 JSON 9223372036854775808 值。

PHP 中没有这样的东西。

对于它的价值,PHP 可以 支持值 > PHP_INT_MAX 使用 bcmath 包 http://php.net/manual/en/book.bc.php 但 JSON 是稍微难一点的问题。

要回答 OP 的问题,即为什么他们不能在 JSON 中将值从字符串编码回 int 类型,答案在于转换步骤。读入原来的JSON字符串时,是一个字符串,逐字节读取。读取值时,它们最初被读取为字符串(如果是字符串,则为 JSON 本身),然后根据句点 (.) 的存在将其转换为 int 或 float 的正确类型。如果该值大于 PHP_INT_MAX,则 PHP 会将其转换为双精度值,并且您会失去精度。因此使用 JSON_BIGINT_AS_STRING 将告诉解析器将值保留为字符串,并且 NOT 尝试转换它,一切都应该是好的,值保持完整,尽管是一个字符串.

问题出现在执行逆操作时,执行 json_encode($value, JSON_NUMERIC_CHECK) 告诉 PHP 将字符串数值转换为 int/float,但这似乎发生在 BEFORE 写入 JSON 字符串,导致值 > PHP_INT_MAX 被转换为双精度表示形式,如 9.2233720368548e+19

参见 https://3v4l.org/lHL62 或以下:

$bigger_than_max = '{"max": ' . PHP_INT_MAX . '1}'; // appending 1 makes > PHP_INT_MAX
var_dump($bigger_than_max);
var_dump(json_decode($bigger_than_max));
var_dump(json_decode($bigger_than_max, false, 512, JSON_BIGINT_AS_STRING));

var_dump(json_encode(json_decode($bigger_than_max, false, 512, JSON_BIGINT_AS_STRING)));
var_dump(json_encode(json_decode($bigger_than_max, false, 512, JSON_BIGINT_AS_STRING), JSON_NUMERIC_CHECK));

结果:

string(29) "{"max": 92233720368547758071}"
object(stdClass)#1 (1) {
  ["max"]=>
  float(9.2233720368548E+19)
}
object(stdClass)#1 (1) {
  ["max"]=>
  string(20) "92233720368547758071"
}
string(30) "{"max":"92233720368547758071"}"
string(29) "{"max":9.223372036854776e+19}"

不幸的是,看起来没有办法解决这个问题,查看 JSON 常量 http://php.net/manual/en/json.constants.php 我没有看到任何允许你写整数值的东西 > PHP_INT_MAX 到 JSON.

中的整数

抱歉,这没有找到解决方案,但希望能消除一些困惑。