在 PHP 中处理大型 JSON 文件

2022-08-30 16:07:47

我正在尝试处理一些大(可能高达200M)的JSON文件。文件的结构基本上是一个对象数组。

因此,大致如下:

[
  {"property":"value", "property2":"value2"},
  {"prop":"val"},
  ...
  {"foo":"bar"}
]

每个对象都有任意属性,并且不必与数组中的其他对象共享它们(如 in 中,具有相同的属性)。

我想对数组中的每个对象应用处理,并且由于文件可能很大,因此我无法在内存中窃取整个文件内容,解码JSON并迭代PHP数组。

因此,理想情况下,我想读取文件,为每个对象获取足够的信息并对其进行处理。如果有类似的库可用于 JSON,则 SAX 类型的方法是可以的。

关于如何最好地处理这个问题的任何建议?


答案 1

我已经为PHP 7编写了一个流式JSON pull parser pcrov/JsonReader,其中包含一个基于XMLReader的api。

它与基于事件的解析器有很大不同,因为您不必设置回调并让解析器执行其操作,而是调用解析器上的方法以根据需要移动或检索数据。找到了所需的位并想要停止解析?然后停止解析(并调用,因为这是一件好事。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 取决于其结构以及您希望对其执行的操作。这些示例应该为您提供一个起点。


答案 2

我决定开发一个基于事件的解析器。它还没有完全完成,当我推出一个令人满意的版本时,它会编辑问题,并链接到我的工作。

编辑:

我终于制定了一个我满意的解析器版本。它在GitHub上可用:

https://github.com/kuma-giyomu/JSONParser

可能还有一些改进的空间,我欢迎反馈。


推荐