skyscanner/jsonpath

此包已被废弃,不再维护。作者建议使用galbar/jsonpath包。

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

3.0 2023-03-05 19:47 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])]}
  • 面向对象实现。
  • 实现getsetadd操作。
  • 实现了魔法方法
    • __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中的Jsonpath返回集合的第一个元素或没有结果时返回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"
  ]
}
JsonPath 结果
$.store.bicycle.price 自行车的价格。
$.store.book[*] 所有书籍。
$.store.book[1,3] 第二本和第四本书。
$.store.book[1:3] 从第二本书到第三本书。
$.store.book[:3] 从第一本书到第三本书。
$.store.book[x:y:z] 从x到y,步长为z的书籍。
$..book[?(@.category == 'fiction')] 所有类别为'fiction'的书籍。
$..*[?(@.available == true)].price 所有可用品的价格。
$..book[?(@.price < 10)].title 所有价格低于10元的书籍的标题。
$..book[?(@.author==$.authors[3])] 所有由“J. R. R. Tolkien”所著的书籍。
$[store] 商店。
$['store'] 商店。
$..book[*][title, 'category', "author"] 所有书籍的标题、类别和作者。
$..book[?(@.author in [$.authors[0], $.authors[2]])] 所有由“Nigel Rees”或“Herman Melville”所著的书籍。
$.store.book[?(@.category == 'fiction' and @.price < 10 or @.color == "red")].price 小说类且价格低于10元或红色的书籍。(and优先于or)

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

测试

要从项目根目录运行测试:
php app/test.php <jsonpath> [<json文件>]

如果没有提供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