klkvsk/json-decode-stream

JSON 流读取器

v1.0.3 2021-06-29 23:00 UTC

This package is auto-updated.

Last update: 2024-08-29 05:30:41 UTC


README

Min PHP version Build Status Scrutinizer tests Scrutinizer coverage Scrutinizer code quality Packagist version

这是一个允许解析 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

    仅对 STRINGNUMBERWHITESPACE 标记返回相应的值。

  • $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 许可证 下分发。