Skip to content

Commit 4825f3d

Browse files
committed
docs(json-path): initiate doc for the component
1 parent 10eb014 commit 4825f3d

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed

components/json_path.rst

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
The JsonPath Component
2+
======================
3+
4+
.. versionadded:: 7.3
5+
6+
The JsonPath component was introduced in Symfony 7.3 as an
7+
:doc:`experimental feature </contributing/code/experimental>`.
8+
9+
The JsonPath component provides a powerful way to query and extract data from
10+
JSON structures. It implements the `RFC 9535 (JSONPath) <https://datatracker.ietf.org/doc/html/rfc9535>`_
11+
standard, allowing you to navigate complex JSON data with ease.
12+
13+
Just as the :doc:`DomCrawler component </components/dom_crawler>` allows you to navigate and query HTML/XML documents
14+
using CSS selectors, the JsonPath component provides a similar fluent interface to traverse and search JSON structures
15+
using JSONPath expressions. It's component also offer an abstraction layer for data extraction.
16+
17+
Installation
18+
------------
19+
20+
You can install the component in your project using Composer:
21+
22+
.. code-block:: terminal
23+
24+
$ composer require symfony/json-path
25+
26+
.. include:: /components/require_autoload.rst.inc
27+
28+
Usage
29+
-----
30+
31+
To start querying a JSON document, first create a :class:`Symfony\\Component\\JsonPath\\JsonCrawler`
32+
object from a JSON string. For the following examples, we'll use this sample
33+
"bookstore" JSON data:
34+
35+
::
36+
37+
use Symfony\Component\JsonPath\JsonCrawler;
38+
39+
$json = <<<'JSON'
40+
{
41+
"store": {
42+
"book": [
43+
{
44+
"category": "reference",
45+
"author": "Nigel Rees",
46+
"title": "Sayings of the Century",
47+
"price": 8.95
48+
},
49+
{
50+
"category": "fiction",
51+
"author": "Evelyn Waugh",
52+
"title": "Sword of Honour",
53+
"price": 12.99
54+
},
55+
{
56+
"category": "fiction",
57+
"author": "Herman Melville",
58+
"title": "Moby Dick",
59+
"isbn": "0-553-21311-3",
60+
"price": 8.99
61+
},
62+
{
63+
"category": "fiction",
64+
"author": "John Ronald Reuel Tolkien",
65+
"title": "The Lord of the Rings",
66+
"isbn": "0-395-19395-8",
67+
"price": 22.99
68+
}
69+
],
70+
"bicycle": {
71+
"color": "red",
72+
"price": 399
73+
}
74+
}
75+
}
76+
JSON;
77+
78+
$crawler = new JsonCrawler($json);
79+
80+
Once you have the crawler instance, use its ``find()`` method to start querying
81+
the data. This method always returns an array of matching values.
82+
83+
Querying with Expressions
84+
-------------------------
85+
86+
The primary way to query the JSON is by passing a JSONPath expression string
87+
to the ``find()`` method.
88+
89+
**Accessing a Specific Property**
90+
91+
Use dot-notation for object keys and square brackets for array indices. The root
92+
of the document is represented by ``$``.
93+
94+
::
95+
96+
// Get the title of the first book in the store
97+
$titles = $crawler->find('$.store.book[0].title');
98+
99+
// $titles is ['Sayings of the Century']
100+
101+
**Searching with the Descendant Operator**
102+
103+
The descendant operator (``..``) recursively searches for a given key, allowing
104+
you to find values without specifying the full path.
105+
106+
::
107+
108+
// Get all authors from anywhere in the document
109+
$authors = $crawler->find('$..author');
110+
111+
// $authors is ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'John Ronald Reuel Tolkien']
112+
113+
**Filtering Results**
114+
115+
JSONPath includes a powerful filter syntax (``?(<expression>)``) to select items
116+
based on a condition. The current item within the filter is referenced by ``@``.
117+
118+
::
119+
120+
// Get all books with a price less than 10
121+
$cheapBooks = $crawler->find('$.store.book[?(@.price < 10)]');
122+
123+
/*
124+
$cheapBooks contains two book objects:
125+
the one by "Nigel Rees" and the one by "Herman Melville"
126+
*/
127+
128+
Building Queries Programmatically
129+
---------------------------------
130+
131+
For more dynamic or complex query building, you can use the fluent API provided
132+
by the :class:`Symfony\\Component\\JsonPath\\JsonPath` class. This allows you
133+
to construct a query object step-by-step. The ``JsonPath`` object can then be
134+
passed to the crawler's ``find()`` method.
135+
136+
The main advantage of the programmatic builder is that it automatically handles
137+
the correct escaping of keys and values, preventing syntax errors.
138+
139+
::
140+
141+
use Symfony\Component\JsonPath\JsonPath;
142+
143+
$path = (new JsonPath())
144+
->key('store') // Selects the 'store' key
145+
->key('book') // Then the 'book' key
146+
->index(1); // Then the item at index 1 (the second book)
147+
148+
// The created $path object is equivalent to the string '$["store"]["book"][1]'
149+
$book = $crawler->find($path);
150+
151+
// $book contains the book object for "Sword of Honour"
152+
153+
The ``JsonPath`` class provides several methods to build your query:
154+
155+
``key(string $name)``
156+
Adds a key selector. The key name will be properly escaped.
157+
158+
::
159+
160+
// Creates the path '$["key\"with\"quotes"]'
161+
$path = (new JsonPath())->key('key"with"quotes');
162+
163+
``deepScan()``
164+
Adds the descendant operator ``..`` to perform a recursive search from the
165+
current point in the path.
166+
167+
::
168+
169+
// Get all prices in the store: '$["store"]..["price"]'
170+
$path = (new JsonPath())->key('store')->deepScan()->key('price');
171+
172+
``all()``
173+
Adds the wildcard operator ``[*]`` to select all items in an array or object.
174+
175+
::
176+
177+
// Creates the path '$["store"]["book"][*]'
178+
$path = (new JsonPath())->key('store')->key('book')->all();
179+
180+
``index(int $index)``
181+
Adds an array index selector.
182+
183+
``first()`` / ``last()``
184+
Shortcuts for ``index(0)`` and ``index(-1)`` respectively.
185+
186+
::
187+
188+
// Get the last book: '$["store"]["book"][-1]'
189+
$path = (new JsonPath())->key('store')->key('book')->last();
190+
191+
``slice(int $start, ?int $end = null, ?int $step = null)``
192+
Adds an array slice selector ``[start:end:step]``.
193+
194+
::
195+
196+
// Get books from index 1 up to (but not including) index 3
197+
// Creates the path '$["store"]["book"][1:3]'
198+
$path = (new JsonPath())->key('store')->key('book')->slice(1, 3);
199+
200+
// Get every second book from the first four books
201+
// Creates the path '$["store"]["book"][0:4:2]'
202+
$path = (new JsonPath())->key('store')->key('book')->slice(0, 4, 2);
203+
204+
``filter(string $expression)``
205+
Adds a filter expression. The expression string is the part that goes inside
206+
the ``?()`` syntax.
207+
208+
::
209+
210+
// Get expensive books: '$["store"]["book"][?(@.price > 20)]'
211+
$path = (new JsonPath())
212+
->key('store')
213+
->key('book')
214+
->filter('@.price > 20');
215+
216+
Advanced Querying
217+
-----------------
218+
219+
For a complete overview of advanced operators like wildcards and functions within
220+
filters, please refer to the `Querying with Expressions`_ section above. All these
221+
features are supported and can be combined with the programmatic builder where
222+
appropriate (e.g., inside a ``filter()`` expression).
223+
224+
Error Handling
225+
--------------
226+
227+
The component will throw specific exceptions for invalid input or queries:
228+
229+
* :class:`Symfony\\Component\\JsonPath\\Exception\\InvalidArgumentException`: Thrown if the input to the ``JsonCrawler`` constructor is not a valid JSON string.
230+
* :class:`Symfony\\Component\\JsonPath\\Exception\\InvalidJsonStringInputException`: Thrown during a ``find()`` call if the JSON string is malformed (e.g., syntax error).
231+
* :class:`Symfony\\Component\\JsonPath\\Exception\\JsonCrawlerException`: Thrown for errors within the JsonPath expression itself, such as using an unknown function.
232+
233+
::
234+
235+
use Symfony\Component\JsonPath\Exception\InvalidJsonStringInputException;
236+
use Symfony\Component\JsonPath\Exception\JsonCrawlerException;
237+
238+
try {
239+
// Example of malformed JSON
240+
$crawler = new JsonCrawler('{"store": }');
241+
$crawler->find('$..*');
242+
} catch (InvalidJsonStringInputException $e) {
243+
// ... handle error
244+
}
245+
246+
try {
247+
// Example of an invalid query
248+
$crawler->find('$.store.book[?unknown_function(@.price)]');
249+
} catch (JsonCrawlerException $e) {
250+
// ... handle error
251+
}

0 commit comments

Comments
 (0)