Skip to content

Commit 55dda22

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

File tree

3 files changed

+110
-7
lines changed

3 files changed

+110
-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.'["'.addcslashes($key, "\"\\\x00..\x1F").'"]');
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: 47 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,47 @@ 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 ['"quotes_only"', '$["\\"quotes_only\\""]'];
85+
yield ['\\\\multiple\\\\backslashes', '$["\\\\\\\\multiple\\\\\\\\backslashes"]'];
86+
yield ["control\x00\x1f\x1echar", '$["control\000\037\036char"]'];
87+
88+
yield ['key"with\\"mixed', '$["key\\"with\\\\\\"mixed"]'];
89+
yield ['\\"complex\\"case\\"', '$["\\\\\\"complex\\\\\\"case\\\\\\""]'];
90+
yield ['json_like":{"value":"test"}', '$["json_like\\":{\\"value\\":\\"test\\"}"]'];
91+
yield ['C:\\Program Files\\"App Name"', '$["C:\\\\Program Files\\\\\\"App Name\\""]'];
92+
93+
yield ['key_with_é_accents', '$["key_with_é_accents"]'];
94+
yield ['unicode_→_arrows', '$["unicode_→_arrows"]'];
95+
yield ['chinese_中文_key', '$["chinese_中文_key"]'];
96+
97+
yield ['', '$[""]'];
98+
yield [' ', '$[" "]'];
99+
yield [' spaces ', '$[" spaces "]'];
100+
yield ["\t\n\r", '$["\\t\\n\\r"]'];
101+
yield ["control\x00char", '$["control\000char"]'];
102+
yield ["newline\nkey", '$["newline\\nkey"]'];
103+
yield ["tab\tkey", '$["tab\\tkey"]'];
104+
yield ["carriage\rreturn", '$["carriage\\rreturn"]'];
64105
}
65106
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy