Обработка больших файлов JSON в PHP
Я пытаюсь обработать несколько больших (возможно, до 200 М) файлов JSON.
Структура файла в основном представляет собой массив объектов.
Итак, что-то вроде:
[
{"property":"value", "property2":"value2"},
{"prop":"val"},
...
{"foo":"bar"}
]
Каждый объект имеет произвольные свойства и не имеет необходимости делиться ими с другими объектами в массиве (как и в случае с тем же).
Я хочу применить обработку к каждому объекту в массиве, и поскольку файл потенциально огромен, я не могу сломать весь файл в памяти, декодировать JSON и итерации по массиву PHP.
В идеале я хотел бы прочитать файл, получить достаточно информации для каждого объекта и обработать его.
Подход типа SAX был бы в порядке, если бы была доступна аналогичная библиотека для JSON.
Любое предложение о том, как лучше справляться с этой проблемой?
Ответы
Ответ 1
Я решил работать над парсером, основанным на событиях. Это еще не совсем сделано и отредактирует вопрос со ссылкой на мою работу, когда я выложу удовлетворительную версию.
EDIT:
Наконец, я разработал версию парсера, которой я доволен. Он доступен на GitHub:
https://github.com/kuma-giyomu/JSONParser
Там, вероятно, есть место для некоторого улучшения и приветствуется обратная связь.
Ответ 2
Я написал потоковый анализатор JSON pcrov/JsonReader для PHP 7 с api на основе XMLReader.
Он существенно отличается от анализаторов, основанных на событиях, в том, что вместо настройки обратных вызовов и разрешения анализатор делает свое дело, вы вызываете методы в синтаксическом анализаторе для перемещения или получения данных по желанию. Нашли нужный бит и хотите прекратить разбор? Затем прекратите синтаксический анализ (и вызовите close()
, потому что это хорошая вещь, чтобы сделать.)
(Для получения более подробного обзора синтаксических разрывов на основе событий см. Модели XML-читателей: SAX и XML-парсер.
Пример 1:
Прочитайте каждый объект целиком из вашего JSON.
use pcrov\JsonReader\JsonReader;
$reader = new JsonReader();
$reader->open("data.json");
$reader->read(); // Outer array.
$depth = $reader->depth(); // Check in a moment to break when the array is done.
$reader->read(); // Step to the first object.
do {
print_r($reader->value()); // Do your thing.
} while ($reader->next() && $reader->depth() > $depth); // Read each sibling.
$reader->close();
Вывод:
Array
(
[property] => value
[property2] => value2
)
Array
(
[prop] => val
)
Array
(
[foo] => bar
)
Объекты возвращаются в виде массивов с строгими ключами (отчасти) до крайних случаев, когда действительный JSON создает имена свойств, которые не допускаются в объектах PHP. Работа над этими конфликтами не стоит, так как анонимный объект stdClass не имеет значения в простейшем массиве.
Пример 2:
Прочитайте каждый именованный элемент отдельно.
$reader = new pcrov\JsonReader\JsonReader();
$reader->open("data.json");
while ($reader->read()) {
$name = $reader->name();
if ($name !== null) {
echo "$name: {$reader->value()}\n";
}
}
$reader->close();
Вывод:
property: value
property2: value2
prop: val
foo: bar
Пример 3:
Прочитайте каждое свойство данного имени. Бонус: чтение из строки вместо URI, а также получение данных из свойств с дублирующимися именами в одном и том же объекте (что разрешено в JSON, как весело.)
$json = <<<'JSON'
[
{"property":"value", "property2":"value2"},
{"foo":"foo", "foo":"bar"},
{"prop":"val"},
{"foo":"baz"},
{"foo":"quux"}
]
JSON;
$reader = new pcrov\JsonReader\JsonReader();
$reader->json($json);
while ($reader->read("foo")) {
echo "{$reader->name()}: {$reader->value()}\n";
}
$reader->close();
Вывод:
foo: foo
foo: bar
foo: baz
foo: quux
Как лучше всего читать ваш JSON зависит от его структуры и того, что вы хотите с ней делать. Эти примеры должны дать вам возможность начать.
Ответ 3
Существует нечто подобное, но только для С++ и Java. Если вы не можете получить доступ к одной из этих библиотек с PHP, для нее нет реализации для PHP, но json_read()
, насколько я знаю. Однако, если json структурирован так просто, легко просто прочитать файл до следующего }
, а затем обработать JSON, полученный через json_read()
. Но вам лучше сделать это буферизированное, например, чтение 10kb, split by}, если не найдено, прочитайте еще 10k, а затем обработайте найденные значения. Затем прочитайте следующий блок и т.д.
Ответ 4
Это простой, потоковый синтаксический анализатор для обработки больших документов JSON. Используйте его для разбора очень больших документов JSON, чтобы не загружать всю вещь в память, а именно, как работает любой другой парсер JSON для PHP.
https://github.com/salsify/jsonstreamingparser
Ответ 5
Существует http://github.com/sfalvo/php-yajl/ Я не использовал его сам.