Skip to content

Commit ebf545c

Browse files
[JsonPath] Always use brackets notation with JsonPath::key()
1 parent cadcc57 commit ebf545c

File tree

3 files changed

+93
-7
lines changed

3 files changed

+93
-7
lines changed

src/Symfony/Component/JsonPath/JsonPath.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function __construct(
3030

3131
public function key(string $key): static
3232
{
33-
return new self($this->path.(str_ends_with($this->path, '..') ? '' : '.').$key);
33+
return new self($this->path.'["'.str_replace(['"', '\\'], ['\"', '\\\\'], $key).'"]');
3434
}
3535

3636
public function index(int $index): static

src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ public function testAllAuthors()
4949
], $result);
5050
}
5151

52+
public function testAllAuthorsWithBrackets()
53+
{
54+
$result = self::getBookstoreCrawler()->find('$..["author"]');
55+
56+
$this->assertCount(4, $result);
57+
$this->assertSame([
58+
'Nigel Rees',
59+
'Evelyn Waugh',
60+
'Herman Melville',
61+
'J. R. R. Tolkien',
62+
], $result);
63+
}
64+
5265
public function testAllThingsInStore()
5366
{
5467
$result = self::getBookstoreCrawler()->find('$.store.*');
@@ -58,6 +71,15 @@ public function testAllThingsInStore()
5871
$this->assertArrayHasKey('color', $result[1]);
5972
}
6073

74+
public function testAllThingsInStoreWithBrackets()
75+
{
76+
$result = self::getBookstoreCrawler()->find('$["store"][*]');
77+
78+
$this->assertCount(2, $result);
79+
$this->assertCount(4, $result[0]);
80+
$this->assertArrayHasKey('color', $result[1]);
81+
}
82+
6183
public function testEscapedDoubleQuotesInFieldName()
6284
{
6385
$crawler = new JsonCrawler(<<<JSON
@@ -77,6 +99,14 @@ public function testBasicNameSelector()
7799
$this->assertSame('Nigel Rees', $result[0]['author']);
78100
}
79101

102+
public function testBasicNameSelectorWithBrackts()
103+
{
104+
$result = self::getBookstoreCrawler()->find('$["store"]["book"]')[0];
105+
106+
$this->assertCount(4, $result);
107+
$this->assertSame('Nigel Rees', $result[0]['author']);
108+
}
109+
80110
public function testAllPrices()
81111
{
82112
$result = self::getBookstoreCrawler()->find('$.store..price');
@@ -121,6 +151,17 @@ public function testBooksWithIsbn()
121151
], [$result[0]['isbn'], $result[1]['isbn']]);
122152
}
123153

154+
public function testBooksWithBracketsAndFilter()
155+
{
156+
$result = self::getBookstoreCrawler()->find('$..["book"][?(@.isbn)]');
157+
158+
$this->assertCount(2, $result);
159+
$this->assertSame([
160+
'0-553-21311-3',
161+
'0-395-19395-8',
162+
], [$result[0]['isbn'], $result[1]['isbn']]);
163+
}
164+
124165
public function testBooksLessThanTenDollars()
125166
{
126167
$result = self::getBookstoreCrawler()->find('$..book[?(@.price < 10)]');
@@ -216,6 +257,14 @@ public function testEverySecondElementReverseSlice()
216257
$this->assertSame([6, 2, 5], $result);
217258
}
218259

260+
public function testEverySecondElementReverseSliceAndBrackets()
261+
{
262+
$crawler = self::getSimpleCollectionCrawler();
263+
264+
$result = $crawler->find('$["a"][::-2]');
265+
$this->assertSame([6, 2, 5], $result);
266+
}
267+
219268
public function testEmptyResults()
220269
{
221270
$crawler = self::getSimpleCollectionCrawler();
@@ -404,6 +453,19 @@ public function testAcceptsJsonPath()
404453
$this->assertSame('red', $result[0]['color']);
405454
}
406455

456+
public function testStarAsKey()
457+
{
458+
$crawler = new JsonCrawler(<<<JSON
459+
{"*": {"a": 1, "b": 2}, "something else": {"c": 3}}
460+
JSON);
461+
462+
$result = $crawler->find('$["*"]');
463+
464+
$this->assertCount(1, $result);
465+
$this->assertSame(['a' => 1, 'b' => 2], $result[0]);
466+
}
467+
468+
407469
private static function getBookstoreCrawler(): JsonCrawler
408470
{
409471
return new JsonCrawler(<<<JSON

src/Symfony/Component/JsonPath/Tests/JsonPathTest.php

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public function testBuildPath()
2323
->index(0)
2424
->key('address');
2525

26-
$this->assertSame('$.users[0].address', (string) $path);
27-
$this->assertSame('$.users[0].address..city', (string) $path->deepScan()->key('city'));
26+
$this->assertSame('$["users"][0]["address"]', (string) $path);
27+
$this->assertSame('$["users"][0]["address"]..["city"]', (string) $path->deepScan()->key('city'));
2828
}
2929

3030
public function testBuildWithFilter()
@@ -33,7 +33,7 @@ public function testBuildWithFilter()
3333
$path = $path->key('users')
3434
->filter('@.age > 18');
3535

36-
$this->assertSame('$.users[?(@.age > 18)]', (string) $path);
36+
$this->assertSame('$["users"][?(@.age > 18)]', (string) $path);
3737
}
3838

3939
public function testAll()
@@ -42,7 +42,7 @@ public function testAll()
4242
$path = $path->key('users')
4343
->all();
4444

45-
$this->assertSame('$.users[*]', (string) $path);
45+
$this->assertSame('$["users"][*]', (string) $path);
4646
}
4747

4848
public function testFirst()
@@ -51,7 +51,7 @@ public function testFirst()
5151
$path = $path->key('users')
5252
->first();
5353

54-
$this->assertSame('$.users[0]', (string) $path);
54+
$this->assertSame('$["users"][0]', (string) $path);
5555
}
5656

5757
public function testLast()
@@ -60,6 +60,30 @@ public function testLast()
6060
$path = $path->key('users')
6161
->last();
6262

63-
$this->assertSame('$.users[-1]', (string) $path);
63+
$this->assertSame('$["users"][-1]', (string) $path);
64+
}
65+
66+
/**
67+
* @dataProvider provideKeysToEscape
68+
*/
69+
public function testEscapedKey(string $key, string $expectedPath)
70+
{
71+
$path = new JsonPath();
72+
$path = $path->key($key);
73+
74+
$this->assertSame($expectedPath, (string) $path);
75+
}
76+
77+
public static function provideKeysToEscape(): iterable
78+
{
79+
yield ['simple_key', '$["simple_key"]'];
80+
yield ['key"with"quotes', '$["key\\\\"with\\\\"quotes"]'];
81+
yield ['path\\backslash', '$["path\\\\backslash"]'];
82+
yield ['mixed\\"case', '$["mixed\\\\\\\\"case"]'];
83+
yield ['unicode_🔑', '$["unicode_🔑"]'];
84+
yield ['', '$[""]'];
85+
yield ['"quotes_only"', '$["\\\\"quotes_only\\\\""]'];
86+
yield ['\\\\multiple\\\\backslashes', '$["\\\\\\\\multiple\\\\\\\\backslashes"]'];
87+
yield ["control\x00\x1f\x1echar", "$[\"control\x00\x1f\x1echar\"]"];
6488
}
6589
}

0 commit comments

Comments
 (0)