【问题标题】:PHP | json_decode huge json filePHP | json_decode 巨大的 json 文件
【发布时间】:2016-10-19 15:53:42
【问题描述】:

我正在尝试解码大型 json 文件 222mb 文件。

我知道我不能通过使用 file_get_contents() 直接使用 json_decode 来读取整个文件并解码整个字符串,因为它会消耗大量内存并且不会返回任何内容(这是它到目前为止所做的。)

所以我去尝试图书馆, 我最近尝试的是JSONParser。 它的作用是在 json 数组中一一读取对象。

但由于那里缺乏文档,我想在这里询问是否有人使用过这个库。

这是来自 github 的示例测试代码

// initialise the parser object
$parser = new JSONParser();

// sets the callbacks
$parser->setArrayHandlers('arrayStart', 'arrayEnd');
$parser->setObjectHandlers('objStart', 'objEnd');
$parser->setPropertyHandler('property');
$parser->setScalarHandler('scalar');
/*
echo "Parsing top level object document...\n";
// parse the document
$parser->parseDocument(__DIR__ . '/data.json');*/

$parser->initialise();

//echo "Parsing top level array document...\n";
// parse the top level array

$parser->parseDocument(__DIR__ . '/array.json');

如何使用循环并将对象保存在 php 变量中,我们可以轻松地将其解码为 php 数组以供进一步使用。

这需要一些时间,因为它会对 json 数组的所有对象一一进行,但问题是如何使用这个库循环它,或者没有这样的选项。

或者还有其他更好的选择或库来完成这项工作吗?

【问题讨论】:

  • 你不应该为你想要的而使用循环。解析器只会发出回调应该处理的事件,并对数据做他们想要/需要做的任何事情。或者有github.com/salsify/jsonstreamingparser。不过,我不能保证任何一个图书馆,所以你必须自己检查一下。

标签: php arrays json


【解决方案1】:

另一种选择是使用halaxa/json-machine

在 JSON 上迭代时的用法与 json_decode 的情况相同,但无论您的文件有多大,它都不会达到内存限制。无需实现任何东西,只需您的foreach

例子:

$users = \JsonMachine\JsonMachine::fromFile('500MB-users.json');

foreach ($users as $id => $user) {
    // process $user as usual
}

更多详情请参阅 github 自述文件。

【讨论】:

  • 是否可以使用halaxa/json-machine 轻松修改大型 JSON 文件的各个部分?例如。使用 JSON 指针语法?谢谢!
  • @tonix 你不能直接修改你正在阅读的文件,但是你可以开始迭代文件A,并将部分一个接一个地写入一个新的文件B。你当然可以在写入之前修改任何部分到新文件中。这样你就可以完成你想要的。唯一的区别是所需的修改最终会出现在一个新文件中。
【解决方案2】:

这里的另一种选择是使用salsify/jsonstreamingparser

您需要创建自己的侦听器。

$testfile = '/path/to/file.json';
$listener = new MyListener();
$stream = fopen($testfile, 'r');
try {
    $parser = new \JsonStreamingParser\Parser($stream, $listener);
    $parser->parse();
    fclose($stream);
} catch (Exception $e) {
    fclose($stream);
    throw $e;
}

为了让事情变得简单易懂,我以这个 json 为例:

JSON 输入

{
    "objects": [
    {
        "propertyInt": 1,
        "propertyString": "string",
        "propertyObject": { "key": "value" }            
    },
    {
        "propertyInt": 2,
        "propertyString": "string2",
        "propertyObject": { "key": "value2" }
    }]
}

您需要实现自己的侦听器。在这种情况下,我只想获取数组中的对象。

PHP

class MyListener extends \JsonStreamingParser\Listener\InMemoryListener
{
    //control variable that allow us to know if is a child or parent object
    protected $level = 0;

    protected function startComplexValue($type)
    {
        //start complex value, increment our level
        $this->level++;
        parent::startComplexValue($type);
    }
    protected function endComplexValue()
    {
        //end complex value, decrement our level
        $this->level--;
        $obj = array_pop($this->stack);
        // If the value stack is now empty, we're done parsing the document, so we can
        // move the result into place so that getJson() can return it. Otherwise, we
        // associate the value
        if (empty($this->stack)) {
            $this->result = $obj['value'];
        } else {
            if($obj['type'] == 'object') {
                //insert value to top object, author listener way
                $this->insertValue($obj['value']);
                //HERE I call the custom function to do what I want
                $this->insertObj($obj);
            }
        }
    }

    //custom function to do whatever
    protected function insertObj($obj)
    {
        //parent object
        if($this->level <= 2) {
          echo "<pre>";
          var_dump($obj);
          echo "</pre>";
        }
    }
}

输出

array(2) {
  ["type"]=>
  string(6) "object"
  ["value"]=>
  array(3) {
    ["propertyInt"]=>
    int(1)
    ["propertyString"]=>
    string(6) "string"
    ["propertyObject"]=>
    array(1) {
      ["key"]=>
      string(5) "value"
    }
  }
}
array(2) {
  ["type"]=>
  string(6) "object"
  ["value"]=>
  array(3) {
    ["propertyInt"]=>
    int(2)
    ["propertyString"]=>
    string(7) "string2"
    ["propertyObject"]=>
    array(1) {
      ["key"]=>
      string(6) "value2"
    }
  }
}

我针对一个 166MB 的 JSON 文件对其进行了测试,它可以正常工作。也许你需要让监听器适应你的需要。

【讨论】:

  • 使用“Guzzle, PHP HTTP client”从url下载json而不加载到内存中。
  • 一个问题,为什么是"$this->insertValue($obj['value']);"必需的。它只会增加内存使用量?如果你不需要完整的输出,你可以去掉它吗?
  • @Felipe Duarte 可以将 Salsify 的 json 流解析器用于大型数组对象而不是文件,例如像数据库结果集中的数组一样?
【解决方案3】:

您仍然需要使用 json_decodefile_get_contents 来获取完整的 JSON(您无法解析部分 JSON)。只需使用 ini_set('memory_limit', '500M'); 将 PHP 的内存限制增加到更大的值

此外,您的处理时间会更长,因此请使用 set_time_limit(0);

【讨论】:

  • 不确定该赞成票来自哪里,但这个答案根本不正确。
  • 我已经使用了ini_set('memory_limit', '-1');,但会使用您建议的时间限制。但是我问的问题是如何循环?我知道我必须使用 json_decode。我也知道我知道我不能在完整文件上使用它。它只能在块等中使用。是的,你的答案不是真的。我们可以解析部分json。有图书馆这样做。
  • @PeeHaa - 为什么这个答案不正确?我在这里阅读它,我真的非常想知道为什么。我应该通过心灵感应读取你的想法,还是应该在没有证据或反驳的情况下盲目相信你的陈述?
  • “我应该通过心灵感应读取你的想法,还是应该在没有证据或反驳的情况下盲目相信你的陈述?” YES oooooooorrrr 只是阅读问题和回购链接?任何适合你的...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-13
  • 1970-01-01
  • 2010-10-29
相关资源
最近更新 更多