Add basic support for using bracket notation in JSON field paths
Previously, only dot notation JSON field paths were supported. Using
dot notation, each key is separated by a '.'. This makes it impossible
to reference a field where the key itself contains a dot.
This commit adds basic bracket notation support to JsonFieldPath.
With this commit, existing functionality remains as is, but users can
now use a basic form of JsonPath bracket notation. This enables the
use of Json keys with embedded dots. i.e. something like :
{
"a.b" : "one"
}
.andDo(document("example",responseFields(fieldWithPath("['a.b']")…
Closes gh-132
This commit is contained in:
@@ -92,8 +92,10 @@ must be compatible with `application/xml`.
|
|||||||
[[documenting-your-api-request-response-payloads-json-field-paths]]
|
[[documenting-your-api-request-response-payloads-json-field-paths]]
|
||||||
===== JSON field paths
|
===== JSON field paths
|
||||||
|
|
||||||
JSON field paths use `.` to descend into a child object and `[]` to identify an array. For
|
JSON field paths use `.` or bracket notation to descend into a child object and `[]` to
|
||||||
example, with this JSON payload:
|
identify an array. Using bracket notation enables the use of `.` within a key name.
|
||||||
|
|
||||||
|
For example, with this JSON payload:
|
||||||
|
|
||||||
[source,json,indent=0]
|
[source,json,indent=0]
|
||||||
----
|
----
|
||||||
@@ -109,7 +111,8 @@ example, with this JSON payload:
|
|||||||
{
|
{
|
||||||
"d":"three"
|
"d":"three"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"e.dot" : "four"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
@@ -126,6 +129,15 @@ The following paths are all present:
|
|||||||
|`a.b`
|
|`a.b`
|
||||||
|An array containing three objects
|
|An array containing three objects
|
||||||
|
|
||||||
|
|`['a']['b']`
|
||||||
|
|An array containing three objects
|
||||||
|
|
||||||
|
|`a['b']`
|
||||||
|
|An array containing three objects
|
||||||
|
|
||||||
|
|`['a'].b`
|
||||||
|
|An array containing three objects
|
||||||
|
|
||||||
|`a.b[]`
|
|`a.b[]`
|
||||||
|An array containing three objects
|
|An array containing three objects
|
||||||
|
|
||||||
@@ -134,6 +146,14 @@ The following paths are all present:
|
|||||||
|
|
||||||
|`a.b[].d`
|
|`a.b[].d`
|
||||||
|The string `three`
|
|The string `three`
|
||||||
|
|
||||||
|
|`a['e.dot']`
|
||||||
|
|The string `four`
|
||||||
|
|
||||||
|
|`['a']['e.dot']`
|
||||||
|
|The string `four`
|
||||||
|
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.restdocs.payload;
|
package org.springframework.restdocs.payload;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -25,10 +25,15 @@ import java.util.regex.Pattern;
|
|||||||
* A path that identifies a field in a JSON payload
|
* A path that identifies a field in a JSON payload
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
|
* @author Jeremy Rickard
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
final class JsonFieldPath {
|
final class JsonFieldPath {
|
||||||
|
|
||||||
|
private static final Pattern BRACKETS_AND_ARRAY_PATTERN = Pattern
|
||||||
|
.compile("\\[\'(.+?)\'\\]|\\[([0-9]+|\\*){0,1}\\]");
|
||||||
|
|
||||||
|
|
||||||
private static final Pattern ARRAY_INDEX_PATTERN = Pattern
|
private static final Pattern ARRAY_INDEX_PATTERN = Pattern
|
||||||
.compile("\\[([0-9]+|\\*){0,1}\\]");
|
.compile("\\[([0-9]+|\\*){0,1}\\]");
|
||||||
|
|
||||||
@@ -76,31 +81,38 @@ final class JsonFieldPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> extractSegments(String path) {
|
private static List<String> extractSegments(String path) {
|
||||||
Matcher matcher = ARRAY_INDEX_PATTERN.matcher(path);
|
Matcher matcher = BRACKETS_AND_ARRAY_PATTERN.matcher(path);
|
||||||
StringBuilder buffer = new StringBuilder();
|
|
||||||
int previous = 0;
|
int previous = 0;
|
||||||
|
|
||||||
|
List<String> tokens = new ArrayList<>();
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
appendWithSeparatorIfNecessary(buffer,
|
if (previous != matcher.start()) {
|
||||||
path.substring(previous, matcher.start(0)));
|
tokens.addAll(expandToken(path.substring(previous, matcher.start())));
|
||||||
appendWithSeparatorIfNecessary(buffer, matcher.group());
|
}
|
||||||
|
if (matcher.group(1) != null) {
|
||||||
|
tokens.add(matcher.group(1));
|
||||||
|
} else {
|
||||||
|
tokens.add(matcher.group());
|
||||||
|
}
|
||||||
previous = matcher.end(0);
|
previous = matcher.end(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous < path.length()) {
|
if (previous < path.length()) {
|
||||||
appendWithSeparatorIfNecessary(buffer, path.substring(previous));
|
tokens.addAll(expandToken(path.substring(previous)));
|
||||||
}
|
}
|
||||||
|
|
||||||
String processedPath = buffer.toString();
|
return tokens;
|
||||||
|
|
||||||
return Arrays.asList(processedPath.indexOf('.') > -1 ? processedPath.split("\\.")
|
|
||||||
: new String[] { processedPath });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendWithSeparatorIfNecessary(StringBuilder buffer,
|
private static List<String> expandToken(String token) {
|
||||||
String toAppend) {
|
String[] tokens = token.split("\\.");
|
||||||
if (buffer.length() > 0 && (buffer.lastIndexOf(".") != buffer.length() - 1)
|
List<String> expandedTokens = new ArrayList<>();
|
||||||
&& !toAppend.startsWith(".")) {
|
for (String aToken : tokens) {
|
||||||
buffer.append(".");
|
if (aToken.length() > 0) {
|
||||||
|
expandedTokens.add(aToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buffer.append(toAppend);
|
return expandedTokens;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.junit.Test;
|
|||||||
* Tests for {@link JsonFieldPath}
|
* Tests for {@link JsonFieldPath}
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
|
* @author Jeremy Rickard
|
||||||
*/
|
*/
|
||||||
public class JsonFieldPathTests {
|
public class JsonFieldPathTests {
|
||||||
|
|
||||||
@@ -110,4 +111,19 @@ public class JsonFieldPathTests {
|
|||||||
contains("[]", "a", "b", "c"));
|
contains("[]", "a", "b", "c"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compilationOfMultipleElementPathWithBrackets() {
|
||||||
|
assertThat(JsonFieldPath.compile("['a']['b']['c']").getSegments(), contains("a", "b", "c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compilationOfMultipleElementPathWithAndWithoutBrackets() {
|
||||||
|
assertThat(JsonFieldPath.compile("['a'][].b['c']").getSegments(), contains("a", "[]", "b", "c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compilationOfMultipleElementPathWithAndWithoutBracketsAndEmbeddedDots() {
|
||||||
|
assertThat(JsonFieldPath.compile("['a.key'][].b['c']").getSegments(), contains("a.key", "[]", "b", "c"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user