remorhaz/php-json-path

PHP 中的 JSONPath 实现

v0.8.1 2024-02-19 08:20 UTC

README

Latest Stable Version Build Scrutinizer Code Quality codecov Mutation testing badge Total Downloads License

JSONPath 是一种简单的 JSON 文档查询语言,受 XPath (XML 的查询语言) 启发,最初由 Stefan Goessner 设计。

特性

  • 接受编码的 JSON 字符串以及解码的 PHP 数据作为输入,同时支持两种表示方式在输出。
  • 使用 JSONPath 查询选择、删除或替换 JSON 文档的部分。
  • 识别明确的/不明确的 JSONPath 查询而不执行它们。
  • 将不明确的 JSONPath 查询转换为针对给定 JSON 文档的明确查询集合。

要求

安装

您可以使用 Composer 安装此包

composer require remorhaz/php-json-path

用法

访问 JSON 文档

您可以使用相应的 节点值工厂 从编码的 JSON 字符串或解码的 JSON 数据创建可访问的 JSON 文档。

use Remorhaz\JSON\Data\Value\EncodedJson;
use Remorhaz\JSON\Data\Value\DecodedJson;

// Creating document from JSON-encoded string:
$encodedValueFactory = EncodedJson\NodeValueFactory::create();
$encodedJson = '{"a":1}';
$document1 = $encodedValueFactory->createValue($encodedJson);

// Creating document from decoded JSON data:
$decodedValueFactory = DecodedJson\NodeValueFactory::create();
$decodedJson = (object) ['a' => 1];
$document2 = $decodedValueFactory->createValue($decodedJson);

创建查询

您应该使用 查询工厂 从 JSONPath 表达式创建查询

use Remorhaz\JSON\Path\Query\QueryFactory;

$queryFactory = QueryFactory::create();

// Creating query that selects all 'a' properties from any document:
$query = $queryFactory->createQuery('$..a');

明确的 查询是定义文档中确切一个路径的查询。如果查询包含任何过滤器、通配符或深层子节点扫描,则被视为 不明确的

可访问的 查询是返回文档未处理部分的查询。如果查询返回聚合函数结果,则被视为 不可访问的

处理查询

您应该使用 查询处理器 的实例在给定的 JSON 文档上执行查询

use Remorhaz\JSON\Path\Processor\Processor;

$processor = Processor::create();

选择 JSON 文档的一部分

使用 JSONPath 查询选择 JSON 文档的部分有两种方法

  1. 您可以使用 ::select() 方法获取所有匹配的部分,这将与 明确的不明确的 查询一起工作。如果文档的任何部分都不匹配您的查询,您将得到一个空数组。
  2. 您可以使用 ::selectOne() 方法获取恰好一个匹配的部分。请注意,这仅适用于 明确的 查询。如果您的查询是不明确的,您将得到一个异常。
use Remorhaz\JSON\Data\Value\EncodedJson;
use Remorhaz\JSON\Path\Processor\Processor;
use Remorhaz\JSON\Path\Query\QueryFactory;

$processor = Processor::create()
$queryFactory = QueryFactory::create();
$encodedValueFactory = EncodedJson\NodeValueFactory::create();

$document = $encodedValueFactory->createValue('{"a":{"a":1,"b":2}');

// Selecting all 'a' properties (indefinite query, values exist):
$query1 = $queryFactory->createQuery('$..a');
$result1 = $processor->select($query1, $document);
var_dump($result1->select()); // array: ['{"a":1,"b":2}', '1']

// Selecting single 'b' property nested in 'a' property (definite query, value exists):
$query2 = $queryFactory->createQuery('$.a.b');
$result2 = $processor->selectOne($query2, $document);
var_dump($result2->exists()); // boolean: true
var_dump($result2->decode()); // integer: 2

// Selecting single 'b' property (definite query, value doesn't exist):
$query3 = $queryFactory->createQuery('$.b');
$result3 = $processor->selectOne($query3, $document);
var_dump($result3->exists()); // boolean: false
var_dump($result3->decode()); // throws exception

请注意,您可以将选择的结果编码为 JSON 字符串或解码为原始 PHP 数据。在访问 ::selectOne() 的结果之前,您可以使用 ::exists() 方法检查其存在性,以避免异常。

删除 JSON 文档的一部分

要删除 JSON 文档的一部分,请使用 ::delete() 方法。它仅适用于 可访问的 查询。如果您的查询是不可访问的,您将得到一个异常。如果没有文档部分匹配查询,您将得到未更改的文档。特殊情况是删除文档的根 - 在这种情况下,您将得到不存在的结果。

use Remorhaz\JSON\Data\Value\EncodedJson;
use Remorhaz\JSON\Path\Processor\Processor;
use Remorhaz\JSON\Path\Query\QueryFactory;

$processor = Processor::create()
$queryFactory = QueryFactory::create();
$encodedValueFactory = EncodedJson\NodeValueFactory::create();

$document = $encodedValueFactory->createValue('{"a":{"a":1,"b":2}');

// Deleting all 'b' properties (value exists):
$query1 = $queryFactory->createQuery('$..b');
$result1 = $processor->delete($query1, $document);
var_dump($result1->exists()); // boolean: true
var_dump($result1->encode()); // '{"a":{"a":1}}'

// Deleting all 'c' properties (value doesn't exist):
$query2 = $queryFactory->createQuery('$..c');
$result2 = $processor->delete($query2, $document);
var_dump($result1->exists()); // boolean: true
var_dump($result1->encode()); // '{"a":{"a":1,"b":2}}'

// Deleting root of the document:
$query3 = $queryFactory->createValue('$');
$result3 = $processor->delete($query3, $document);
var_dump($result3->exists()); // boolean: false
var_dump($result3->encode()); // throws exception

使用另一个 JSON 文档替换 JSON 文档的一部分

要用另一个JSON文档替换JSON文档的某部分,请使用::replace()方法。此方法仅适用于可寻址查询。如果您的查询不可寻址,您将收到异常。如果没有文档部分与查询匹配,您将获得未更改的文档。如果查询匹配文档的嵌套部分,您也将收到异常。

use Remorhaz\JSON\Data\Value\EncodedJson;
use Remorhaz\JSON\Path\Processor\Processor;
use Remorhaz\JSON\Path\Query\QueryFactory;

$processor = Processor::create()
$queryFactory = QueryFactory::create();
$encodedValueFactory = EncodedJson\NodeValueFactory::create();

$document1 = $encodedValueFactory->createValue('{"a":{"a":1,"b":2}');
$document2 = $encodedValueFactory->createValue('{"c":3}');

// Replacing 'a' property (value exists):
$query1 = $queryFactory->createQuery('$.a');
$result1 = $processor->replace($query1, $document1, $document2);
var_dump($result1->encode()); // string: '{"a":{"c":3}}'

// Replacing all 'c' properties (value doesn't exist)
$query2 = $queryFactory->createQuery('$..c');
$result2 = $processor->replace($query2, $document1, $document2);
var_dump($result2->encode()); // string: '{"a":{"a":1,"b":2}'

// Replacing all 'a' properties (values are nested):
$query3 = $queryFactory->createQuery('$..a');
$result3 = $processor->replace($query3, $document1, $document2); // throws exception

语法

所有JSONPath查询都以抽象的$符号开头,该符号表示外部级别对象。内部结构可以通过子运算符和过滤器进行匹配

子运算符

选择结构子项有两种表示法:表示法和括号表示法。

点表示法允许选择确切的一个属性或所有子项(使用通配符)。双点表示法递归地遍历JSON结构。

括号表示法允许选择一组属性/元素

聚合函数

可以将聚合函数附加到查询中的任何路径,并将返回计算值。

聚合函数集合和这一想法取自Java实现

过滤器表达式

当过滤器应用于节点集时,它仅保留那些表达式求值为真的节点。

过滤器上下文

表达式@指向应用过滤器时的值。

运算符

比较运算符可以用于将值与另一个值或文字进行比较。支持的运算符有:==!=>>=<<=。括号可用于分组,并且还支持逻辑运算符&&||!。可以使用=~运算符进行正则表达式匹配。

原始定义

Goessner通过提供一组对JSON样本的示例查询来描述JSONPath语法。以下是他的原始数据样本

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

以下是他的原始示例查询及其结果描述