Skip to content

Commit 87ab604

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]")
1 parent 4858eea commit 87ab604

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

src/main/java/com/redislabs/modules/rejson/Path.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public static Path of(final String strPath) {
6060
return new Path(strPath);
6161
}
6262

63+
public static Path ofJsonPointer(final String strPath) {
64+
return new Path(parse(strPath));
65+
}
66+
6367
@Override
6468
public boolean equals(Object obj) {
6569
if (obj == null) return false;
@@ -72,4 +76,88 @@ public boolean equals(Object obj) {
7276
public int hashCode() {
7377
return strPath.hashCode();
7478
}
79+
80+
private static String parse(String path) {
81+
if (path == null) {
82+
throw new NullPointerException("Json Pointer Path cannot be null.");
83+
}
84+
if (path.isEmpty()) {
85+
throw new IllegalArgumentException("Json Pointer Path cannot be empty.");
86+
}
87+
if (path.charAt(0) != '/') {
88+
throw new IllegalArgumentException("Json Pointer Path must start with '/'.");
89+
}
90+
if (path.length() == 1) {
91+
// only contains '/'
92+
return ROOT_PATH.toString();
93+
}
94+
char[] ary = path.toCharArray();
95+
StringBuilder r = new StringBuilder(ary.length);
96+
StringBuilder builder = new StringBuilder();
97+
boolean num = true;
98+
char prev = '/';
99+
for (int i = 1; i < ary.length; i++) {
100+
char c = ary[i];
101+
switch (c) {
102+
case '~':
103+
if (prev == '~') {
104+
num = false;
105+
builder.append('~');
106+
}
107+
break;
108+
case '/':
109+
if (prev == '~') {
110+
num = false;
111+
builder.append('~');
112+
}
113+
if (builder.length() > 0 && num) {
114+
r.append(".[" + builder.toString() + "]");
115+
} else {
116+
r.append("." + builder.toString());
117+
}
118+
num = true;
119+
builder.setLength(0);
120+
break;
121+
case '0':
122+
if (prev == '~') {
123+
num = false;
124+
builder.append("~");
125+
} else {
126+
builder.append(c);
127+
}
128+
break;
129+
case '1':
130+
if (prev == '~') {
131+
num = false;
132+
builder.append("/");
133+
} else {
134+
builder.append(c);
135+
}
136+
break;
137+
default:
138+
if (prev == '~') {
139+
num = false;
140+
builder.append('~');
141+
}
142+
if (c < '0' || c > '9') {
143+
num = false;
144+
}
145+
builder.append(c);
146+
break;
147+
}
148+
prev = c;
149+
}
150+
if (prev == '~') {
151+
num = false;
152+
builder.append("~");
153+
}
154+
if (builder.length() > 0) {
155+
if (num) {
156+
r.append(".[" + builder.toString() + "]");
157+
} else {
158+
r.append("." + builder.toString());
159+
}
160+
}
161+
return r.toString();
162+
}
75163
}

src/test/java/com/redislabs/modules/rejson/PathTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,22 @@ public void testPathHashCode() {
3131
assertEquals(Path.of(".a.b.c").hashCode(), Path.of(".a.b.c").hashCode());
3232
assertNotEquals(Path.of(".a.b.c").hashCode(), Path.of(".b.c").hashCode());
3333
}
34+
35+
@Test
36+
public void testJsonPointer() {
37+
assertEquals(Path.ofJsonPointer("/"), Path.ROOT_PATH);
38+
assertEquals(Path.ofJsonPointer("/a/b/c"), Path.of(".a.b.c"));
39+
assertEquals(Path.ofJsonPointer("/a/0/c"), Path.of(".a.[0].c"));
40+
assertEquals(Path.ofJsonPointer("/a/0b/c"), Path.of(".a.0b.c"));
41+
assertEquals(Path.ofJsonPointer("/ab/cd/1010"), Path.of(".ab.cd.[1010]"));
42+
assertEquals(Path.ofJsonPointer("/a/b/c").hashCode(), Path.of(".a.b.c").hashCode());
43+
44+
//escape test
45+
assertEquals(Path.ofJsonPointer("/a/~0/c"), Path.of(".a.~.c"));
46+
assertEquals(Path.ofJsonPointer("/a/~1/c"), Path.of(".a./.c"));
47+
assertEquals(Path.ofJsonPointer("/a/~~/c"), Path.of(".a.~~.c"));
48+
assertEquals(Path.ofJsonPointer("/~/~~~/~~"), Path.of(".~.~~~.~~"));
49+
assertEquals(Path.ofJsonPointer("/~/~~~0/~~"), Path.of(".~.~~~.~~"));
50+
assertEquals(Path.ofJsonPointer("/~/'.'/~~"), Path.of(".~.'.'.~~"));
51+
}
3452
}

0 commit comments

Comments
 (0)