klkvsk / json-decode-stream
JSON 流读取器
Requires
- php: >=7.1
- ext-json: *
Requires (Dev)
- nyholm/psr7: ^1.3
- phpunit/phpunit: *
- psr/http-message: ^1.0
- vimeo/psalm: *
README
这是一个允许解析 JSON 数据流的 JSON 解析库。您可以在不先将整个结构解码到内存中的情况下即时处理 JSON 记录。这在解析大型 JSON 文件时特别有用。
安装
composer require klkvsk/json-decode-stream
基本用法
在大多数情况下,流式解析器用于解析重复对象的列表。例如,这是一个用户列表
{ "users": [ { "name": "Alice", "age": 20 }, { "name": "Bob", "age": 30 } ] }
要迭代每个用户并打印他们的名字,请使用
$parser = \JsonDecodeStream\Parser::fromFile("users.json"); foreach ($parser->items("users[]") as $user) { echo $user->name; } // or foreach ($parser->items("users[].name") as $name) { echo $name; }
文档
json-decode-stream 使用分层生成器来处理数据。共有 3 层,处理过程如下
Tokenizer -(tokens)-> Parser -(events)-> Collector -(items)-> your code
- 令牌 是编码 JSON 数据的组成部分:花括号、逗号、字符串、数字。
- 事件 根据传入令牌的序列发出:对象开始/结束、指定键、值等。
- 项 是解码 JSON 的最终部分:标量值、数组和对象,它们与 选择器 匹配。
Parser 类直接提供对每一层的访问,但对于大多数用例,只需 $parser->items($selector)
。
选择器
选择器是一个指定收集 JSON 字段的完整路径的字符串。简单选择器包括
[]
- 数组的任何元素或对象的每个键[5]
- 索引为 5 的数组元素[10:15]
- 索引在 10 到 15 之间的数组中的任何元素[5:]
- 从索引 5 开始的数组中的任何元素[:5]
- 在索引 5 及之前的数组中的任何元素foo
- 对象中键 'foo' 的值["some long key with spaces or \"quotes\" in it"]
- 也是一个键的值
选择器可以嵌套
users[]
会选择每个用户result.total
会选择 "result" 对象中的键 "total"result[]
会选择 "total" 的值和 "result" 对象中的其他字段users[:2].name
会选择前三个用户的姓名[].id
会选择具有 "id" 字段的顶层对象数组中的每个 ID
参考
解析器
构造函数
-
Parser::fromString($jsonString)
-
Parser::fromFile($filePath)
-
Parser::fromStream($resource)
其中
$resource
是支持fread()
的任何资源 -
Parser::fromPsr7($stream)
其中
$stream
是任何符合 PSR-7 的 StreamInterface,即$psr7Request->getBody()
-
new Parser(new SourceBuffer(new SourceInterfaceImplementation()))
在其他自定义情况下的使用
方法
-
$parser->tokens(): Generator
遍历编码 JSON 文档,返回
Token
对象。 -
$parser->events(): Generator
遍历
tokens()
,返回Event
对象。 -
$parser->items($selectors): Generator
遍历
events()
,返回与选择器匹配的解码 JSON 字段其中
$selectors
可以是- 单个选择器字符串:
"result.users[]"
- 逗号分隔的选择器字符串:
"result.total, result.users[]"
- 选择器字符串数组:
[ "result.total", "result.users[]" ]
- 自定义 CollectorInterface 或它们的数组
null
用于收集 JSON 序列中的整个对象/数组(在源中以逗号或换行符分隔)
字符串选择器在内部转换为
Collector
类。对于默认收集器,迭代的值在其键中有完整路径,如
"result.users[4]" => ["num" => "Five", ..]
- 单个选择器字符串:
事件
方法
-
$event->getId(): 字符串
枚举
Event::DOCUMENT_START
Event::DOCUMENT_END
事件::OBJECT_START
事件::OBJECT_END
事件::KEY
事件::VALUE
-
$event->getValue(): string|number|bool|null
- 对于
事件::VALUE
返回相应的值。 - 对于
事件::KEY
返回一个字符串(对象的字段名)。 - 对于其他事件返回 null。
- 对于
-
$event->getPath(): string
当前解析元素的完整路径。
-
$event->getDepth(): int
我们深入到 JSON 结构的嵌套层数有多少。顶级数组/对象的元素深度为 1。
-
$event->matchPath(string $selector): bool
检查当前解析元素的路径是否包含在选择器中。
-
$event->getLineNumber(): int
和$event->getCharNumber(): int
返回当前解析在解码源内的位置。
标记
-
$token->getId(): string
枚举
标记::OBJECT_START
标记::OBJECT_END
标记::ARRAY_START
标记::ARRAY_END
标记::KEY_DELIMITER
标记::COMA
标记::TRUE
标记::FALSE
标记::NULL
标记::STRING
标记::NUMBER
标记::WHITESPACE
-
$token->getValue(): string|number|bool|null
仅对
STRING
、NUMBER
或WHITESPACE
标记返回相应的值。 -
$token->getLineNumber(): int
和$token->getCharNumber(): int
返回当前解析在解码源内的位置。
自定义收集器
CollectorInterface
定义了唯一的一个方法
processEvent(Event $event)
当你需要发出一个项目时,返回一个 [ key, value ]
数组,将从 items()
中产生。
当你需要发出多个项目时,产生多个 [ key, value ]
。
如果没有要产生的内容,则返回 null。
以下是一个自定义收集器的示例
class AggregationCollector implements CollectorInterface { protected int $count; protected float $sum; public function processEvent(Event $event) { switch ($event->getId()) { case Event::DOCUMENT_START: $this->count = 0; $this->sum = 0; break; case Event::VALUE: if ($event->matchPath("games[].score")) { $this->sum += $event->getValue(); $this->count++; } break; case Event::DOCUMENT:END: yield [ 'count', $this->count ]; yield [ 'sum', $this->sum ]; yield [ 'avg', $this->count ? ($this->sum / $this->count) : 0 ]; break; } } } $aggregates = iterator_to_array($parser->items(new AggregationCollector())); var_dump($aggregates); // [ 'count' => 10, 'sum' => 50, 'avg' => 5 ]
依赖关系
除了通常随每个 PHP 发行版一起提供的 ext-json
以外,没有其他外部依赖。
默认使用 json_decode
解析单个 JSON 字符串。当解析器找到它们时。这比编写自己的 JSON 字符串解析器/验证器更快、更安全。
测试
此库被单元测试和 CI-tested 在 7.1 版本以来的所有 PHP 版本中全面覆盖。
要运行测试,通过 composer 安装 --dev 并运行
$ vendor/bin/phpunit
许可证
此代码在 MIT 许可证 下分发。