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

Posted

技术标签:

【中文标题】json_decode AND json_encode 长整数而不丢失数据【英文标题】:json_decode AND json_encode long integers without losing data 【发布时间】:2015-08-15 21:13:49 【问题描述】:

php 文档中所述,当json_decodeing 包含长整数的数据结构时,它们将被转换为浮点数。解决方法是使用JSON_BIGINT_AS_STRING,它将它们保留为字符串。当json_encode 输入这些值时,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 例程似乎已经过时了。

【问题讨论】:

你检查过类似的线程***.com/questions/15659325/… 您的要求有点令人困惑。总结一下:你想json_decode一个数据集,改变一些属性,然后再json_encode它;你的问题是大整数这样做时要么变成浮点数或字符串。我说对了吗? 可以,但建议使用第三方库。我喜欢让我的应用程序很小。 @deceze:是的,你是对的。我认为 json_decode 不应该将 bigint 转换为字符串或浮点数。发回修改后的对象时,服务器对无效的数据类型说“不”。 如何在 json 中将大整数作为字符串发送? 【参考方案1】:

只要你的 PHP 版本实际上可以处理大整数,这意味着如果你运行的是 64 位版本的 PHP (在某些东西上 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 时会产生歧义。

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

PHP 中没有内置这样的东西。

【讨论】:

我使用的是 windows 8.1(64 位)、apache v2.4(64 位)和 php v5.6.9(64 位)。 好吧,警告:"64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit." 呃,我觉得没有办法。但我希望你赢得声誉。谢谢你。我的生产系统使用的是 linux,那里没问题。 像“38 位精度”这样更大的整数呢【参考方案2】:

对于它的价值,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,但这似乎发生在 在写入 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 中的整数的东西。

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

【讨论】:

以上是关于json_decode AND json_encode 长整数而不丢失数据的主要内容,如果未能解决你的问题,请参考以下文章