galbar/jsonpath

用于查询和更新 JSON 对象的 JSONPath 实现

3.0 2023-03-05 19:47 UTC

This package is auto-updated.

Last update: 2024-08-27 14:37:23 UTC


README

Build Status Coverage Status Latest Stable Version License

这是 PHP 的 JSONPath 实现。

此实现包含规范中除 () 操作符之外的所有元素(规范中有 $..a[(@.length-1)],但可以通过 $..a[-1] 实现,后者更简单)。

如果查询没有匹配项,它将返回一个空数组。

功能

此实现具有以下功能

  • 正则表达式匹配键(例如 $.*[?(/^bo{2}k$/)]$[?(/a\wthors/)])。
  • 正则表达式匹配值比较(例如 $.store.book[?(@.author =~ /.*Tolkien/)]
  • 对于子操作符 [],除非字段名称是无效的 JavaScript 变量名称,否则无需将子名称用引号括起来(例如 $[store][book, bicycle])。
  • 可以使用 .length 获取字符串的长度、获取数组的长度以及检查节点是否有子节点。
  • in 操作符允许在指定的列表中过滤值:$..[?(@.author in ["Nigel Rees", "Evelyn Waugh", $.store.book[3].author])]
  • 面向对象实现。
  • 获取设置添加 操作。
  • 实现了魔法方法
    • __get: $obj->{'$...'}
    • __set: $obj->{'$...'} = $val
    • __toString: echo $obj 打印 JsonObject 的 JSON 表示形式。
  • 不使用 eval()

安装

要安装 JsonPath,您需要在项目中使用 Composer。有关安装信息,请参阅文档

composer require galbar/jsonpath

用法

在您使用的每个文件中添加以下内容

use JsonPath\JsonObject;

现在您可以创建 JsonObject 的一个实例并使用它

// $json can be a string containing json, a PHP array, a PHP object or null.
// If $json is null (or not present) the JsonObject will be empty.
$jsonObject = new JsonObject();
// or
$jsonObject = new JsonObject($json);

// get
$obj->get($jsonPath);
$obj->{'$.json.path'};

// set
$obj->set($jsonPath, $value);
$obj->{'$.json.path'} = $value;

// get the json representation
$obj->getJson();
$str = (string)$obj;
echo $obj;

// get the PHP array representation
$obj->getValue();

// add values
$obj->add($jsonPath, $value[, $field]);

// remove values
$obj->remove($jsonPath, $field);

SmartGet

在创建 JsonObject 的新实例时,您可以向构造函数传递第二个参数。这会将实例的行为设置为使用 SmartGet。

SmartGet 做的是确定给定的 JsonPath 在某个点上是否有分支,如果有,则按常规行为处理;否则,它将直接返回给定路径指向的值(而不是包含它的数组)或如果没有找到,则返回 false

GetJsonObjects

有时您需要访问子对象中的多个值,该子对象具有长的前缀(例如 $.a.very.long.prefix.for.[*].object),在这种情况下,您首先获取该对象并创建它的 JsonObject,然后访问其属性。

现在,如果您想编辑对象(设置或添加值)并希望这些更改影响原始对象,则可以通过使用 JsonObject::getJsonObjects($jsonpath) 来实现。此方法与 get 的行为相同,但它将结果作为包含对源 JsonObject 中值的引用的 JsonObject 实例返回。

JsonPath 语言

此库实现了以下规范

var_name¹   = /^\.([\p{L}\p{N}\_\$][\p{L}\p{N}\_\-\$]*|\*)(.*)/u
number      = ([0-9]+(\.[0-9]*) | ([0-9]*\.[0-9]+))
string      = ('\''.*?'\'' | '"'.*?'"')
boolean     = ('true' | 'false')
regpattern  = '/'.*?'/i?x?'
null        = 'null'
index       = -?[0-9]+

jsonpath    = '$' operator*
childpath   = '@' operator*

operator    = (childname | childfilter | recursive) operator*

childname   = '.' (var_name | '*')
childfilter = '[' ('*' | namelist | indexlist | arrayslice | filterexpr) ']'
recursive   = '..' (var_name | childfilter | '*')

namelist    = var_name (',' (var_name | '\'' .*? '\'' | '"' .*? '"'))*
indexlist   = index (',' index)*
arrayslice  = index? ':' index? ':' index?
filterexpr  = '?(' ors ' | regpattern)'

ors         = ands (' ' ( 'or' | '\|\|' ) ' ' ands)*
ands        = expr (' ' ( 'and' | '&&' ) ' ' expr)*
expr        = ( 'not ' | '! ' )? (value | comp | in_array)
comp        = value ('==' | '!=' | '<' | '>' | '<=' | '>=' | '=~') value
value       = (jsonpath | childpath | number | string | boolean | regpattern | null | length)
length      = (jsonpath | childpath) '.length'
in_array    = value 'in' '[' value (',' value)* ']'

¹var_name:正则表达式大致翻译为“任何有效的 JavaScript 变量名称”,包括一些怪癖,例如以数字或包含破折号(-)开头的名称。

规范限制

  • value 内部的 jsonpath 不能包含 orand 或任何比较器。
  • value 中的 Jsonpaths 返回集合的第一个元素或如果没有结果则返回 false
  • 布尔运算不能与括号一起分组。
  • “与”运算符(and)的优先级高于“或”运算符(or)。这意味着 a and 1 = b or c != d 等价于 (a and 1) or (c != d)

.length 操作符 可以用来

  • 获取 JsonObject 中一个节点的子节点数量:$..*[?(@.length > 3)]
  • 筛选具有子节点的节点:$..*[?(@.length)]
  • 或者筛选没有子节点(叶子节点)的节点:$..*[?(not @.length)]
  • 检查字符串的长度:$.path.to[?(@.a.string.length > 10)]
  • 获取字符串的长度:$.path.to.field.length
  • 获取数组的长度:$.path.to.array.length
  • 获取数组中数组的长度:$.path.to.array[*].array[*].length
  • 获取字符串数组中字符串的长度:$.path.to.array[*].array[*].key.length

比较运算符:
==!=<><=>=按预期工作(按类型和值进行比较)。
=~ 是一个正则表达式比较运算符,将左操作数与右操作数中的模式进行匹配。左边的值必须是 字符串,右边的值是 正则表达式模式。否则返回 false

JsonPath 示例

考虑以下 json

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

更多示例请参阅 ./tests/Galbar/JsonPath 文件夹。

测试

要运行测试,从项目根目录
php app/test.php <jsonpath> [<file to json file>]

如果没有提供 json 文件,则默认为在此文件中之前描述的 json 对象。

例如
php app/test.php "$..*[?(@.category == 'fiction' and @.price < 10 or @.color == \"red\")].price"
结果应该是
[19.95,8.99]

准备编码

您可以在浏览器中打开项目并开始编码 Gitpod ready-to-code

文档

要生成文档,从项目根目录
php vendor/bin/sami.php update app/docgen.php