Skip to content

Commit e18598c

Browse files
committed
Add RFC6901 json pointer support
Now Path can convert RFC6901 style path to json path for example Path.ofJosnPointer("/abc/0") can convert to Path.of(".[\"abc\"].[0]") the limitation of JsonPointer path is /abc/0 always convert to .[\"abc\"].[0] and not .[\"abc\"].[\"0\"] if users want to use .abc.0, they should use Path.of(".abc.0") instead of Path.ofJosnPointer("/abc/0")
1 parent 80d01d2 commit e18598c

File tree

5 files changed

+168
-0
lines changed

5 files changed

+168
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package redis.clients.jedis.json;
2+
3+
import java.util.Objects;
4+
5+
/**
6+
* an RFC6901 implementation that convert json pointer path to json path.
7+
*/
8+
class JsonPointer {
9+
10+
static String parse(String path, String rootPath) {
11+
Objects.requireNonNull(path, "Json Pointer Path cannot be null.");
12+
13+
if (path.isEmpty()) {
14+
// "" means all document
15+
return rootPath;
16+
}
17+
if (path.charAt(0) != '/') {
18+
throw new IllegalArgumentException("Json Pointer Path must start with '/'.");
19+
}
20+
21+
final char[] ary = path.toCharArray();
22+
StringBuilder result = new StringBuilder();
23+
StringBuilder builder = new StringBuilder();
24+
boolean number = true;
25+
char prev = '/';
26+
for (int i = 1; i < ary.length; i++) {
27+
char c = ary[i];
28+
switch (c) {
29+
case '~':
30+
if (prev == '~') {
31+
number = false;
32+
builder.append('~');
33+
}
34+
break;
35+
case '/':
36+
if (prev == '~') {
37+
number = false;
38+
builder.append('~');
39+
}
40+
if (builder.length() > 0 && number) {
41+
result.append(".[").append(builder).append("]");
42+
} else {
43+
result.append(".[\"").append(builder).append("\"]");
44+
}
45+
number = true;
46+
builder.setLength(0);
47+
break;
48+
case '0':
49+
if (prev == '~') {
50+
number = false;
51+
builder.append("~");
52+
} else {
53+
builder.append(c);
54+
}
55+
break;
56+
case '1':
57+
if (prev == '~') {
58+
number = false;
59+
builder.append("/");
60+
} else {
61+
builder.append(c);
62+
}
63+
break;
64+
default:
65+
if (prev == '~') {
66+
number = false;
67+
builder.append('~');
68+
}
69+
if (c < '0' || c > '9') {
70+
number = false;
71+
}
72+
builder.append(c);
73+
break;
74+
}
75+
prev = c;
76+
}
77+
if (prev == '~') {
78+
number = false;
79+
builder.append("~");
80+
}
81+
if (builder.length() > 0) {
82+
if (number) {
83+
result.append(".[").append(builder).append("]");
84+
} else {
85+
result.append(".[\"").append(builder).append("\"]");
86+
}
87+
} else if (prev == '/') {
88+
result.append(".[\"").append(builder).append("\"]");
89+
}
90+
return result.toString();
91+
}
92+
}

src/main/java/redis/clients/jedis/json/Path.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public String toString() {
2121
public static Path of(final String strPath) {
2222
return new Path(strPath);
2323
}
24+
25+
public static Path ofJsonPointer(final String strPath) {
26+
return new Path(JsonPointer.parse(strPath, ROOT_PATH.strPath));
27+
}
2428

2529
@Override
2630
public boolean equals(Object obj) {

src/main/java/redis/clients/jedis/json/Path2.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public String toString() {
3333
public static Path2 of(final String path) {
3434
return new Path2(path);
3535
}
36+
37+
public static Path2 ofJsonPointer(final String path) {
38+
return new Path2(JsonPointer.parse(path, ROOT_PATH.str));
39+
}
3640

3741
@Override
3842
public boolean equals(Object obj) {

src/test/java/redis/clients/jedis/modules/json/Path2Test.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,38 @@ public void equals() {
4040
assertTrue(new Path2("a.b").equals(Path2.of(".a.b")));
4141
assertTrue(Path2.of("a.b").equals(new Path2(".a.b")));
4242
}
43+
44+
@Test
45+
public void testJsonPointer() {
46+
assertEquals(Path2.ofJsonPointer(""), Path2.ROOT_PATH);
47+
assertEquals(Path2.ofJsonPointer("/"), Path2.of("$.[\"\"]"));
48+
assertEquals(Path2.ofJsonPointer("//0"), Path2.of("$.[\"\"].[0]"));
49+
assertEquals(Path2.ofJsonPointer("//"), Path2.of("$.[\"\"].[\"\"]"));
50+
assertEquals(Path2.ofJsonPointer("// "), Path2.of("$.[\"\"].[\" \"]"));
51+
assertEquals(Path2.ofJsonPointer("/a/b/c"), Path2.of("$.[\"a\"].[\"b\"].[\"c\"]"));
52+
assertEquals(Path2.ofJsonPointer("/a/0/c"), Path2.of("$.[\"a\"].[0].[\"c\"]"));
53+
assertEquals(Path2.ofJsonPointer("/a/0b/c"), Path2.of("$.[\"a\"].[\"0b\"].[\"c\"]"));
54+
assertEquals(Path2.ofJsonPointer("/ab/cd/1010"), Path2.of("$.[\"ab\"].[\"cd\"].[1010]"));
55+
assertEquals(Path2.ofJsonPointer("/a/b/c").hashCode(), Path2.of("$.[\"a\"].[\"b\"].[\"c\"]").hashCode());
56+
57+
// escape test
58+
assertEquals(Path2.ofJsonPointer("/a/~0"), Path2.of("$.[\"a\"].[\"~\"]"));
59+
assertEquals(Path2.ofJsonPointer("/a/~1"), Path2.of("$.[\"a\"].[\"/\"]"));
60+
assertEquals(Path2.ofJsonPointer("/a/~0/c"), Path2.of("$.[\"a\"].[\"~\"].[\"c\"]"));
61+
assertEquals(Path2.ofJsonPointer("/a/~1/c"), Path2.of("$.[\"a\"].[\"/\"].[\"c\"]"));
62+
assertEquals(Path2.ofJsonPointer("/a/~~/c"), Path2.of("$.[\"a\"].[\"~~\"].[\"c\"]"));
63+
assertEquals(Path2.ofJsonPointer("/~/~~~/~"), Path2.of("$.[\"~\"].[\"~~~\"].[\"~\"]"));
64+
assertEquals(Path2.ofJsonPointer("/~/~~~/~~"), Path2.of("$.[\"~\"].[\"~~~\"].[\"~~\"]"));
65+
assertEquals(Path2.ofJsonPointer("/~/~~~0/~~"), Path2.of("$.[\"~\"].[\"~~~\"].[\"~~\"]"));
66+
assertEquals(Path2.ofJsonPointer("/~/'.'/~~"), Path2.of("$.[\"~\"].[\"'.'\"].[\"~~\"]"));
67+
68+
// json path escape test
69+
assertEquals(Path2.ofJsonPointer("/\t"), Path2.of("$.[\"\t\"]"));
70+
assertEquals(Path2.ofJsonPointer("/\u0074"), Path2.of("$.[\"\u0074\"]"));
71+
assertEquals(Path2.ofJsonPointer("/'"), Path2.of("$.[\"'\"]"));
72+
assertEquals(Path2.ofJsonPointer("/\'"), Path2.of("$.[\"\'\"]"));
73+
assertEquals(Path2.ofJsonPointer("/\""), Path2.of("$.[\"\"\"]"));
74+
assertEquals(Path2.ofJsonPointer("/\n"), Path2.of("$.[\"\n\"]"));
75+
assertEquals(Path2.ofJsonPointer("/\\"), Path2.of("$.[\"\\\"]"));
76+
}
4377
}

src/test/java/redis/clients/jedis/modules/json/PathTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,38 @@ public void testPathHashCode() {
3333
assertEquals(Path.of(".a.b.c").hashCode(), Path.of(".a.b.c").hashCode());
3434
assertNotEquals(Path.of(".a.b.c").hashCode(), Path.of(".b.c").hashCode());
3535
}
36+
37+
@Test
38+
public void testJsonPointer() {
39+
assertEquals(Path.ofJsonPointer(""), Path.ROOT_PATH);
40+
assertEquals(Path.ofJsonPointer("/"), Path.of(".[\"\"]"));
41+
assertEquals(Path.ofJsonPointer("//0"), Path.of(".[\"\"].[0]"));
42+
assertEquals(Path.ofJsonPointer("//"), Path.of(".[\"\"].[\"\"]"));
43+
assertEquals(Path.ofJsonPointer("// "), Path.of(".[\"\"].[\" \"]"));
44+
assertEquals(Path.ofJsonPointer("/a/b/c"), Path.of(".[\"a\"].[\"b\"].[\"c\"]"));
45+
assertEquals(Path.ofJsonPointer("/a/0/c"), Path.of(".[\"a\"].[0].[\"c\"]"));
46+
assertEquals(Path.ofJsonPointer("/a/0b/c"), Path.of(".[\"a\"].[\"0b\"].[\"c\"]"));
47+
assertEquals(Path.ofJsonPointer("/ab/cd/1010"), Path.of(".[\"ab\"].[\"cd\"].[1010]"));
48+
assertEquals(Path.ofJsonPointer("/a/b/c").hashCode(), Path.of(".[\"a\"].[\"b\"].[\"c\"]").hashCode());
49+
50+
// escape test
51+
assertEquals(Path.ofJsonPointer("/a/~0"), Path.of(".[\"a\"].[\"~\"]"));
52+
assertEquals(Path.ofJsonPointer("/a/~1"), Path.of(".[\"a\"].[\"/\"]"));
53+
assertEquals(Path.ofJsonPointer("/a/~0/c"), Path.of(".[\"a\"].[\"~\"].[\"c\"]"));
54+
assertEquals(Path.ofJsonPointer("/a/~1/c"), Path.of(".[\"a\"].[\"/\"].[\"c\"]"));
55+
assertEquals(Path.ofJsonPointer("/a/~~/c"), Path.of(".[\"a\"].[\"~~\"].[\"c\"]"));
56+
assertEquals(Path.ofJsonPointer("/~/~~~/~"), Path.of(".[\"~\"].[\"~~~\"].[\"~\"]"));
57+
assertEquals(Path.ofJsonPointer("/~/~~~/~~"), Path.of(".[\"~\"].[\"~~~\"].[\"~~\"]"));
58+
assertEquals(Path.ofJsonPointer("/~/~~~0/~~"), Path.of(".[\"~\"].[\"~~~\"].[\"~~\"]"));
59+
assertEquals(Path.ofJsonPointer("/~/'.'/~~"), Path.of(".[\"~\"].[\"'.'\"].[\"~~\"]"));
60+
61+
// json path escape test
62+
assertEquals(Path.ofJsonPointer("/\t"), Path.of(".[\"\t\"]"));
63+
assertEquals(Path.ofJsonPointer("/\u0074"), Path.of(".[\"\u0074\"]"));
64+
assertEquals(Path.ofJsonPointer("/'"), Path.of(".[\"'\"]"));
65+
assertEquals(Path.ofJsonPointer("/\'"), Path.of(".[\"\'\"]"));
66+
assertEquals(Path.ofJsonPointer("/\""), Path.of(".[\"\"\"]"));
67+
assertEquals(Path.ofJsonPointer("/\n"), Path.of(".[\"\n\"]"));
68+
assertEquals(Path.ofJsonPointer("/\\"), Path.of(".[\"\\\"]"));
69+
}
3670
}

0 commit comments

Comments
 (0)