Как сериализовать большие объекты / массивы в JSON

Мое приложение должно производить JSON объекта, который имеет большой data свойство типа массив. Массив должен оставаться в памяти, поскольку он собирает выходные данные БД, и некоторые свойства могут быть определены только после того, как массив будет завершен.

Осложнение: массив основан на числах и должен отображаться как таковой в выводе json, поэтому должен быть прямым json_encode() это не вариант.

Чтобы сделать это возможным на машинах с низкой спецификацией, таких как RasPi, я рассмотрел вопрос об уменьшении потребления памяти:

  1. использование SPLFixedArray
  2. использование string а также pack()

Оба подхода решают проблему с памятью хранения массивов, но терпят неудачу, когда дело доходит до кодирования в JSON.

Я смотрел на реализацию JsonSerializable но так как это заставляет пользователей возвращать результат, который затем закодирован в Json, я вернулся к

public function jsonSerialize() {
    return $this->toArray();
}

у которого те же проблемы с памятью.

zendframework/Component_ZendJson выглядит многообещающе, так как ищет объекты, имеющие toJson() способ обеспечить свою собственную кодировку как stringвместо object,

Мне интересно, есть ли лучшие варианты, которые не дают проблем с памятью?

2 ответа

Решение

В своем исследовании я рассмотрел 5 различных подходов для хранения больших массивов кортежей в памяти, которые обобщены здесь с их результатами (с выборкой по 50 тыс. Записей):

  1. наивный

    Экспортировать json просто с помощью json_encode, используя array(array(), array())

    Память: 18,5 МБ (огромная)
    Время: ~100 мс для сборки и выгрузки массива (Windows PC)

  2. Библиотека SPL

    Этот подход хранит все во вложенных SPLFixedArrays: SPLFixedArray[SPLFixedArray], Экспорт в JSON завершен Zend\Json\Encoder путем реализации toJson метод.

    Память: 15,5 МБ (по- прежнему большой)
    Время: ~ 1,3 с, х10 медленнее

  3. Библиотека SPL

    Похоже на 2, но вместо внутреннего SPLFixedArray использует упакованные строки из PHP pack() функция.

    Память: 3,5 МБ (в 5 раз меньше)
    Время: ~ 1,3 с, х10 медленнее - по-видимому pack() так же медленно, как вложенный массив.

  4. Библиотека SPL

    Похоже на 2, но вместо внутреннего SPLFixedArray фактические кортежи просто записываются в виде последовательных значений в корневой массив.

    Память: 3,25 МБ (опять же меньше)
    Время: ~0,7 с, только в 6 раз медленнее - у нас здесь есть победитель?

  5. pack()

    Похоже на 3, но вместо рута SPLFixedArray упаковать все в одну строку, используя PHP pack() функция. Очевидно, что для этого нужны знания и фиксированная идентичная структура отдельных массивов.

    Память: 1,25 МБ (очень маленькая - только 1/12 от первоначальной памяти)
    Время: ~ 1,7 с, х16 медленнее

ЗАКЛЮЧЕНИЕ

Хотя (5) предлагает лучшее использование памяти, он также чрезвычайно медленный. Для моих целей я остановился на (4), который составляет около 20% от первоначального потребления памяти, но - с учетом кодирования JSON - также в 5-6 раз медленнее. Приемлемый компромисс.

По данным json.org:

JSON построен на двух структурах:

  • Коллекция пар имя / значение. На разных языках это реализовано как объект, запись, структура, словарь, хеш-таблица, список ключей или ассоциативный массив.
  • Упорядоченный список значений. В большинстве языков это реализовано как массив, вектор, список или последовательность.

Я не знаю, есть ли проблемы с памятью с этим, но рассмотрим следующий код:

<?php
// No declared index causes no index in JSON
$arr = array('dssdf','38904uj');
echo json_encode($arr).'<br><br>';
// ["dssdf","38904uj"]

// start array at 0 removes the index from JSON
$arr = array('0'=>'dssdf','1'=>'38904uj');
echo json_encode($arr).'<br><br>';
// ["dssdf","38904uj"]

// start array at 1 forces the index to show in JSON
$arr = array('1'=>'dssdf','2'=>'38904uj');
echo json_encode($arr).'<br><br>';
// {"1":"dssdf","2":"38904uj"}

// skip an index forces the index to show in JSON
$arr = array('0'=>'dssdf','1'=>'38904uj','3'=>'321as5d4');
echo json_encode($arr).'<br><br>';
// {"0":"dssdf","1":"38904uj","3":"321as5d4"}

// JSON_FORCE_OBJECT option forces indexes
$arr = array('0'=>'dssdf','1'=>'38904uj');
echo json_encode($arr, JSON_FORCE_OBJECT).'<br><br>';
// {"0":"dssdf","1":"38904uj"}
?>
Другие вопросы по тегам