в JSON и JSON кодирования декодирования длинных целых чисел без потери данных


Как отмечено в документации PHP, когда json_decode ing структура данных, содержащая длинные целые числа, они будут преобразованы в плавающие значения. Обходной путь заключается в использовании 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 больше не имеет проблем с большими ints, но процедура json_decode кажется устаревшей.

2   6   2015-06-02 10:29:50

2 ответа:

До тех пор, пока ваша версия PHP действительно может обрабатывать большие целые числа, то есть если вы используете 64-разрядную версию PHP (на чем-то , кроме 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.

Для чего это стоит, PHP может поддерживать значения > PHP_INT_MAX с помощью пакета bcmath http://php.net/manual/en/book.bc.php но JSON-это несколько более сложная проблема.

Чтобы ответить на вопрос OP о том, почему они не могут кодировать значение из строки обратно в тип int в JSON, ответ лежит в шаге преобразования. При чтении исходной строки JSON в, это строка, и читать байт за байтом. При чтении значений они первоначально считываются как строка (как сам JSON, если строка), а затем приведение к правильному типу к int или float в зависимости от наличия точки (.). Если значение больше PHP_INT_MAX, то PHP преобразует его в double, и вы теряете точность. Таким образом, используя JSON_BIGINT_AS_STRING скажет синтаксическому анализатору сохранить значение в виде строки и не пытаться привести его, все должно быть хорошо, значение сохраняется в такт, хотя и строка.

Проблема возникает, когда выполняется обратное, и doing 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 в ints внутри JSON.

Извините, что это не находит решения, но, надеюсь, проясняет некоторую путаницу.