这是另一个'走树'问题。 JSON streaming parser读取源文件并开始构建'树'。它通过收集“元素”并将它们存储在内存中来实现这一点。为了使我们能够处理每个条目,它在方便的时候“发布事件”。这意味着它会根据需要调用你的函数传递有用的值。 '树事件' 的
的例子是:
那么,我们如何确定一个'完整的事件'?
输入文件由一个数组组成,其中每个条目是JSON的“obbject”。每个对象由组成'对象'数据的'子条目'组成。
现在,当我们遍历'树'构建它时,我们的代码将在如上所示的各个点处被调用。特别是当对象和数组的“开始”和“结束”时。我们需要收集“外部对象”的所有数据。
我们如何识别?随着处理过程的进行,我们记录下“树”中的位置。我们通过跟踪树中“嵌套”的深度来做到这一点。因此,'水平'。一个对象的'开始''嵌套'一个层次,一个对象的'结束''突破'一个层次。
我们感兴趣的对象是'1级'。
提供的代码: 1)跟踪'等级',并在达到'等级1'的对象末尾时调用我们的函数。 2)从'level 1'处的对象的开始累积适当结构中的数据。
要求:
1)称之为“可赎回”程序时,有一个“完整的事件”,可以进行处理。
假设:
处理:
- 解析文件
- 每当当前事件是 '完整'
- 执行 '的processEvent' 调用与访问当前事件。
源代码:
代码:index.php文件
<?php // https://stackoverflow.com/questions/31079129/how-to-handle-nested-objects-in-processing-a-json-stream
require_once __DIR__ .'/vendor/jsonstreamingparser/src/JsonStreamingParser/Parser.php';
require_once __DIR__ .'/vendor/jsonstreamingparser/src/JsonStreamingParser/Listener/IdleListener.php';
require_once __DIR__ .'/Q31079129Listener.php';
/**
* The input file consists of a JSON array of 'Events'.
*
* The important point is that when the file is being 'parsed' the 'listener' is
* 'walking' the tree.
*
* Therefore
* 1) Each 'Event' is at 'level 1' in the tree.
*
* Event Level Changes:
* Start: level will go from 1 => 2
* End: level will go from 2 => 1 !!!!
*
* Actions:
* The 'processEvent' function will be called when the
* 'Event Level' changes to 2 from 1.
*
*/
define('JSON_FILE', __DIR__. '/Q31079129.json');
/**
* This is called when one 'Event' is complete
*
* @param type $listener
*/
function processEvent($listener) {
echo '<pre>', '+++++++++++++++';
print_r($listener->get_event());
echo '</pre>';
}
// ----------------------------------------------------------------------
// the 'Listener'
$listener = new Q31079129Listener();
// setup the 'Event' Listener that will be called with each complete 'Event'
$listener->whenLevelAction = 'processEvent';
// process the input stream
$stream = fopen(JSON_FILE, 'r');
try {
$parser = new JsonStreamingParser_Parser($stream, $listener);
$parser->parse();
}
catch (Exception $e) {
fclose($stream);
throw $e;
}
fclose($stream);
exit;
代码:Q31079129Listener.php
<?php // // https://stackoverflow.com/questions/31079129/how-to-handle-nested-objects-in-processing-a-json-stream
/**
* This is the supplied example modified:
*
* 1) Record the current 'depth' of 'nesting' in the current object being parsed.
*/
class Q31079129Listener extends JsonStreamingParser\Listener\IdleListener {
public $whenLevelAction = null;
protected $event;
protected $prevLevel;
protected $level;
private $_stack;
private $_keys;
public function get_event() {
return $this->event;
}
public function get_prevLevel() {
return $this->prevLevel;
}
public function get_level() {
return $this->prevLevel;
}
public function start_document() {
$this->prevLevel = 0;
$this->level = 0;
$this->_stack = array();
$this->_keys = array();
// echo '<br />start of document';
}
public function end_document() {
// echo '<br />end of document';
}
public function start_object() {
$this->prevLevel = $this->level;
$this->level++;
$this->_start_complex_value('object');
}
public function end_object() {
$this->prevLevel = $this->level;
$this->level--;
$this->_end_complex_value();
}
public function start_array() {
$this->prevLevel = $this->level;
$this->level++;
$this->_start_complex_value('array');
}
public function end_array() {
$this->prevLevel = $this->level;
$this->level--;
$this->_end_complex_value();
}
public function key($key) {
$this->_keys[] = $key;
}
public function value($value) {
$this->_insert_value($value);
}
private function _start_complex_value($type) {
// We keep a stack of complex values (i.e. arrays and objects) as we build them,
// tagged with the type that they are so we know how to add new values.
$current_item = array('type' => $type, 'value' => array());
$this->_stack[] = $current_item;
}
private function _end_complex_value() {
$obj = array_pop($this->_stack);
// If the value stack is now at level 1 from level 2,
// we're done parsing the current complete event, so we can
// move the result into place so that get_event() can return it. Otherwise, we
// associate the value
// var_dump(__FILE__.__LINE__, $this->prevLevel, $this->level, $obj);
if ($this->prevLevel == 2 && $this->level == 1) {
if (!is_null($this->whenLevelAction)) {
$this->event = $obj['value'];
call_user_func($this->whenLevelAction, $this);
$this->event = null;
}
}
else {
$this->_insert_value($obj['value']);
}
}
// Inserts the given value into the top value on the stack in the appropriate way,
// based on whether that value is an array or an object.
private function _insert_value($value) {
// Grab the top item from the stack that we're currently parsing.
$current_item = array_pop($this->_stack);
// Examine the current item, and then:
// - if it's an object, associate the newly-parsed value with the most recent key
// - if it's an array, push the newly-parsed value to the array
if ($current_item['type'] === 'object') {
$current_item['value'][array_pop($this->_keys)] = $value;
} else {
$current_item['value'][] = $value;
}
// Replace the current item on the stack.
$this->_stack[] = $current_item;
}
}
的解决方案将读取数据流的两倍。您第一次阅读所有强制性属性。第二次处理与会者并发出邀请(这不是很优雅,但只有流,并且您控制了内存限制) – Mat
嗯,我对JSON结构有一些控制权。我担心的是,生成JSON的客户端可能不会像对象属性的排序那样控制事物......所以当他们告诉他们的代码将对象转换为JSON时,对象属性顺序可能不可控。 –
iam_decoder - 感谢您的编辑! –