diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index 0a07f13e..7fd84cdf 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -92,10 +92,14 @@ must be compatible with `application/xml`. [[documenting-your-api-request-response-payloads-json-field-paths]] ===== JSON field paths -JSON field paths use `.` or bracket notation to descend into a child object and `[]` to -identify an array. Using bracket notation enables the use of `.` within a key name. +JSON field paths use either dot notation or bracket notation. Dot notation uses '.' to +separate each key in the path; `a.b`, for example. Bracket notation wraps each key in +square brackets and single quotes; `['a']['b']`, for example. In either case, `[]` is used +to identify an array. Dot notation is more concise, but using bracket notation enables the +use of `.` within a key name; `['a.b']`, for example. The two different notations can be +used in the same path; `a['b']`, for example. -For example, with this JSON payload: +With this JSON payload: [source,json,indent=0] ---- diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java index e546a115..aca86bcb 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java @@ -33,7 +33,6 @@ 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 .compile("\\[([0-9]+|\\*){0,1}\\]"); @@ -85,34 +84,35 @@ final class JsonFieldPath { int previous = 0; - List tokens = new ArrayList<>(); + List segments = new ArrayList<>(); while (matcher.find()) { if (previous != matcher.start()) { - tokens.addAll(expandToken(path.substring(previous, matcher.start()))); + segments.addAll(extractDotSeparatedSegments(path.substring(previous, + matcher.start()))); } if (matcher.group(1) != null) { - tokens.add(matcher.group(1)); - } else { - tokens.add(matcher.group()); + segments.add(matcher.group(1)); + } + else { + segments.add(matcher.group()); } previous = matcher.end(0); } if (previous < path.length()) { - tokens.addAll(expandToken(path.substring(previous))); + segments.addAll(extractDotSeparatedSegments(path.substring(previous))); } - return tokens; + return segments; } - private static List expandToken(String token) { - String[] tokens = token.split("\\."); - List expandedTokens = new ArrayList<>(); - for (String aToken : tokens) { - if (aToken.length() > 0) { - expandedTokens.add(aToken); + private static List extractDotSeparatedSegments(String path) { + List segments = new ArrayList<>(); + for (String segment : path.split("\\.")) { + if (segment.length() > 0) { + segments.add(segment); } } - return expandedTokens; + return segments; } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java index bc61a338..35ed2d64 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java @@ -111,7 +111,8 @@ public class JsonFieldProcessorTests { List>> alpha = Arrays.asList( Arrays.asList(entry1, entry2), Arrays.asList(entry3)); payload.put("a", alpha); - assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[][].id"), payload), + assertThat( + this.fieldProcessor.extract(JsonFieldPath.compile("a[][].id"), payload), equalTo((Object) Arrays.asList("1", "2", "3"))); } @@ -124,15 +125,15 @@ public class JsonFieldProcessorTests { List>> alpha = Arrays.asList( Arrays.asList(entry1, entry2), Arrays.asList(entry3)); payload.put("a", alpha); - assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[][].ids"), payload), - equalTo((Object) Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3), - Arrays.asList(4)))); + assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[][].ids"), + payload), equalTo((Object) Arrays.asList(Arrays.asList(1, 2), + Arrays.asList(3), Arrays.asList(4)))); } @Test(expected = FieldDoesNotExistException.class) public void nonExistentTopLevelField() { - this.fieldProcessor - .extract(JsonFieldPath.compile("a"), new HashMap()); + this.fieldProcessor.extract(JsonFieldPath.compile("a"), + new HashMap()); } @Test(expected = FieldDoesNotExistException.class) @@ -216,6 +217,17 @@ public class JsonFieldProcessorTests { assertThat(payload.size(), equalTo(0)); } + @Test + public void extractNestedEntryWithDotInKeys() throws IOException { + Map payload = new HashMap<>(); + Map alpha = new HashMap<>(); + payload.put("a.key", alpha); + alpha.put("b.key", "bravo"); + assertThat(this.fieldProcessor.extract( + JsonFieldPath.compile("['a.key']['b.key']"), payload), + equalTo((Object) "bravo")); + } + private Map createEntry(String... pairs) { Map entry = new HashMap<>(); for (String pair : pairs) {