commit | author | age
|
2207d6
|
1 |
JSONPath |
W |
2 |
============= |
|
3 |
|
|
4 |
This is a [JSONPath](http://goessner.net/articles/JsonPath/) implementation for PHP based on Stefan Goessner's JSONPath script. |
|
5 |
|
|
6 |
JSONPath is an XPath-like expression language for filtering, flattening and extracting data. |
|
7 |
|
|
8 |
I believe that is improves on the original script (which was last updated in 2007) by doing a few things: |
|
9 |
|
|
10 |
- Object-oriented code (should be easier to manage or extend in future) |
|
11 |
- Expressions are parsed into tokens using some code cribbed from Doctrine Lexer and cached |
|
12 |
- There is no `eval()` in use |
|
13 |
- Performance is pretty much the same |
|
14 |
- Any combination of objects/arrays/ArrayAccess-objects can be used as the data input which is great if you're de-serializing JSON in to objects |
|
15 |
or if you want to process your own data structures. |
|
16 |
|
|
17 |
JSONPath Examples |
|
18 |
--- |
|
19 |
|
|
20 |
JSONPath | Result |
|
21 |
--------------------------|------------------------------------- |
|
22 |
`$.store.books[\*].author` | the authors of all books in the store |
|
23 |
`$..author` | all authors |
|
24 |
`$.store..price` | the price of everything in the store. |
|
25 |
`$..books[2]` | the third book |
|
26 |
`$..books[(@.length-1)]` | the last book in order. |
|
27 |
`$..books[0,1]` | the first two books |
|
28 |
`$..books[:2]` | the first two books |
|
29 |
`$..books[?(@.isbn)]` | filter all books with isbn number |
|
30 |
`$..books[?(@.price<10)]` | filter all books cheapier than 10 |
|
31 |
`$..*` | all elements in the data (recursively extracted) |
|
32 |
|
|
33 |
|
|
34 |
Expression syntax |
|
35 |
--- |
|
36 |
|
|
37 |
Symbol | Description |
|
38 |
----------------------|------------------------- |
|
39 |
`$` | The root object/element (not strictly necessary) |
|
40 |
`@` | The current object/element |
|
41 |
`.` or `[]` | Child operator |
|
42 |
`..` | Recursive descent |
|
43 |
`*` | Wildcard. All child elements regardless their index. |
|
44 |
`[,]` | Array indices as a set |
|
45 |
`[start:end:step]` | Array slice operator borrowed from ES4/Python. |
|
46 |
`?()` | Filters a result set by a script expression |
|
47 |
`()` | Uses the result of a script expression as the index |
|
48 |
|
|
49 |
PHP Usage |
|
50 |
--- |
|
51 |
|
|
52 |
```php |
|
53 |
$data = ['people' => [['name' => 'Joe'], ['name' => 'Jane'], ['name' => 'John']]]; |
|
54 |
$result = (new JSONPath($data))->find('$.people.*.name'); // returns new JSONPath |
|
55 |
// $result[0] === 'Joe' |
|
56 |
// $result[1] === 'Jane' |
|
57 |
// $result[2] === 'John' |
|
58 |
``` |
|
59 |
|
|
60 |
### Magic method access |
|
61 |
|
|
62 |
The options flag `JSONPath::ALLOW_MAGIC` will instruct JSONPath when retrieving a value to first check if an object |
|
63 |
has a magic `__get()` method and will call this method if available. This feature is *iffy* and |
|
64 |
not very predictable as: |
|
65 |
|
|
66 |
- wildcard and recursive features will only look at public properties and can't smell which properties are magically accessible |
|
67 |
- there is no `property_exists` check for magic methods so an object with a magic `__get()` will always return `true` when checking |
|
68 |
if the property exists |
|
69 |
- any errors thrown or unpredictable behaviour caused by fetching via `__get()` is your own problem to deal with |
|
70 |
|
|
71 |
```php |
|
72 |
$jsonPath = new JSONPath($myObject, JSONPath::ALLOW_MAGIC); |
|
73 |
``` |
|
74 |
|
|
75 |
For more examples, check the JSONPathTest.php tests file. |
|
76 |
|
|
77 |
Script expressions |
|
78 |
------- |
|
79 |
|
|
80 |
Script expressions are not supported as the original author intended because: |
|
81 |
|
|
82 |
- This would only be achievable through `eval` (boo). |
|
83 |
- Using the script engine from different languages defeats the purpose of having a single expression evaluate the same way in different |
|
84 |
languages which seems like a bit of a flaw if you're creating an abstract expression syntax. |
|
85 |
|
|
86 |
So here are the types of query expressions that are supported: |
|
87 |
|
|
88 |
[?(@._KEY_ _OPERATOR_ _VALUE_)] // <, >, !=, and == |
|
89 |
Eg. |
|
90 |
[?(@.title == "A string")] // |
|
91 |
[?(@.title = "A string")] |
|
92 |
// A single equals is not an assignment but the SQL-style of '==' |
|
93 |
|
|
94 |
Similar projects |
|
95 |
---------------- |
|
96 |
|
|
97 |
[JMESPath](https://github.com/jmespath) does similiar things, is full of features and has a PHP implementation |
|
98 |
|
|
99 |
The [Hash](http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html) utility from CakePHP does some similar things |
|
100 |
|
|
101 |
The original JsonPath implementations is available at [http://code.google.com/p/jsonpath]() and re-hosted for composer |
|
102 |
here [Peekmo/JsonPath](https://github.com/Peekmo/JsonPath). |
|
103 |
|
|
104 |
[ObjectPath](http://objectpath.org) ([https://github.com/adriank/ObjectPath]()) appears to be a Python/JS implementation |
|
105 |
with a new name and extra features. |
|
106 |
|
|
107 |
Changelog |
|
108 |
--------- |
|
109 |
|
|
110 |
### 0.3.0 |
|
111 |
- Added JSONPathToken class as value object |
|
112 |
- Lexer clean up and refactor |
|
113 |
- Updated the lexing and filtering of the recursive token ("..") to allow for a combination of recursion |
|
114 |
and filters, eg. $..[?(@.type == 'suburb')].name |
|
115 |
|
|
116 |
### 0.2.1 - 0.2.5 |
|
117 |
- Various bug fixes and clean up |
|
118 |
|
|
119 |
### 0.2.0 |
|
120 |
- Added a heap of array access features for more creative iterating and chaining possibilities |
|
121 |
|
|
122 |
### 0.1.x |
|
123 |
- Init |