diff --git a/README.md b/README.md index 0c1b32a6e..45e320599 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ String json = "{\"date_as_long\" : 1411455611975}"; Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class); ``` -If you configure JsonPath to use `JacksonMappingProvider`, `GsonMappingProvider`, or `JakartaJsonProvider` you can even +If you configure JsonPath to use `JacksonMappingProvider`, `Jackson3MappingProvider`, `GsonMappingProvider`, or `JakartaJsonProvider` you can even map your JsonPath output directly into POJO's. ```java @@ -434,6 +434,8 @@ JsonPath is shipped with five different JsonProviders: * [JsonSmartJsonProvider](https://github.com/netplex/json-smart-v2) (default) * [JacksonJsonProvider](https://github.com/FasterXML/jackson) * [JacksonJsonNodeJsonProvider](https://github.com/FasterXML/jackson) +* [JacksonJson3Provider](https://github.com/FasterXML/jackson) +* [JacksonJson3NodeJsonProvider](https://github.com/FasterXML/jackson) * [GsonJsonProvider](https://code.google.com/p/google-gson/) * [JsonOrgJsonProvider](https://github.com/stleary/JSON-java) * [JakartaJsonProvider](https://javaee.github.io/jsonp/) @@ -464,7 +466,7 @@ Configuration.setDefaults(new Configuration.Defaults() { }); ``` -Note that the JacksonJsonProvider requires `com.fasterxml.jackson.core:jackson-databind:2.4.5` and the GsonJsonProvider +Note that the JacksonJsonProvider requires `com.fasterxml.jackson.core:jackson-databind:2.20.1`, the Jackson3JsonProvider requires `tools.jackson.core:jackson-databind:3.0.3` and the GsonJsonProvider requires `com.google.code.gson:gson:2.3.1` on your classpath. Both of Jakarta EE 9 [JSON-P (JSR-342)](https://javaee.github.io/jsonp/) and [JSON-B (JSR-367)](http://json-b.net/) diff --git a/build.gradle b/build.gradle index 85518a869..734621edc 100644 --- a/build.gradle +++ b/build.gradle @@ -12,16 +12,17 @@ buildscript { ext { libs = [ - jsonSmart : 'net.minidev:json-smart:2.6.0', - slf4jApi : 'org.slf4j:slf4j-api:2.0.17', - gson : 'com.google.code.gson:gson:2.13.2', - hamcrest : 'org.hamcrest:hamcrest:3.0', - jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.19.2', - jettison : 'org.codehaus.jettison:jettison:1.5.4', - jsonOrg : 'org.json:json:20250517', - tapestryJson : 'org.apache.tapestry:tapestry-json:5.9.0', - jakartaJsonP : 'jakarta.json:jakarta.json-api:2.1.3', - jakartaJsonB : 'jakarta.json.bind:jakarta.json.bind-api:2.0.0', + jsonSmart : 'net.minidev:json-smart:2.6.0', + slf4jApi : 'org.slf4j:slf4j-api:2.0.17', + gson : 'com.google.code.gson:gson:2.13.2', + hamcrest : 'org.hamcrest:hamcrest:3.0', + jacksonDatabind : 'com.fasterxml.jackson.core:jackson-databind:2.20.1', + jacksonDatabind3: 'tools.jackson.core:jackson-databind:3.0.3', + jettison : 'org.codehaus.jettison:jettison:1.5.4', + jsonOrg : 'org.json:json:20250517', + tapestryJson : 'org.apache.tapestry:tapestry-json:5.9.0', + jakartaJsonP : 'jakarta.json:jakarta.json-api:2.1.3', + jakartaJsonB : 'jakarta.json.bind:jakarta.json.bind-api:2.0.0', test : [ 'commons-io:commons-io:2.20.0', @@ -33,7 +34,8 @@ ext { 'org.slf4j:slf4j-simple:2.0.17', 'com.google.code.gson:gson:2.13.2', 'org.hamcrest:hamcrest:3.0', - 'com.fasterxml.jackson.core:jackson-databind:2.19.2', + 'com.fasterxml.jackson.core:jackson-databind:2.20.1', + 'tools.jackson.core:jackson-databind:3.0.3', 'org.codehaus.jettison:jettison:1.5.4', 'org.json:json:20250517', 'org.apache.tapestry:tapestry-json:5.9.0', diff --git a/json-path/build.gradle b/json-path/build.gradle index b8a07ccfb..a75bcc140 100644 --- a/json-path/build.gradle +++ b/json-path/build.gradle @@ -13,6 +13,7 @@ dependencies { implementation libs.jsonSmart implementation libs.slf4jApi compileOnly libs.jacksonDatabind // , optional + compileOnly libs.jacksonDatabind3 // , optional compileOnly libs.gson// , optional compileOnly libs.jsonOrg// , optional compileOnly libs.tapestryJson// , optional diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonNodeJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonNodeJsonProvider.java new file mode 100644 index 000000000..779a23e6e --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonNodeJsonProvider.java @@ -0,0 +1,294 @@ +package com.jayway.jsonpath.spi.json; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import com.jayway.jsonpath.InvalidJsonException; +import com.jayway.jsonpath.JsonPathException; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; +import tools.jackson.databind.node.StringNode; + +public class Jackson3JsonNodeJsonProvider extends AbstractJsonProvider { + + private static final ObjectMapper defaultObjectMapper = new ObjectMapper(); + + protected ObjectMapper objectMapper; + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + /** + * Initialize the JacksonJsonNodeJsonProvider with the default ObjectMapper and ObjectReader + */ + public Jackson3JsonNodeJsonProvider() { + this(defaultObjectMapper); + } + + /** + * Initialize the JacksonJsonNodeJsonProvider with a custom ObjectMapper and ObjectReader. + * + * @param objectMapper the ObjectMapper to use + */ + public Jackson3JsonNodeJsonProvider(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public Object parse(String json) throws InvalidJsonException { + try { + return objectMapper.readTree(json); + } catch (JacksonException e) { + throw new InvalidJsonException(e, json); + } + } + + @Override + public Object parse(byte[] json) throws InvalidJsonException { + try { + return objectMapper.readTree(json); + } catch (JacksonException e) { + throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8)); + } + } + + @Override + public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException { + try { + return objectMapper.readTree(new InputStreamReader(jsonStream, charset)); + } catch (IOException e) { + throw new InvalidJsonException(e); + } + } + + @Override + public String toJson(Object obj) { + if (!(obj instanceof JsonNode)) { + throw new JsonPathException("Not a JSON Node"); + } + return obj.toString(); + } + + @Override + public Object createArray() { + return JsonNodeFactory.instance.arrayNode(); + } + + @Override + public Object createMap() { + return JsonNodeFactory.instance.objectNode(); + } + + public Object unwrap(Object o) { + if (o == null) { + return null; + } + if (!(o instanceof JsonNode)) { + return o; + } + + JsonNode e = (JsonNode) o; + + if (e.isValueNode()) { + + if (e.isString()) { + return e.asString(); + } else if (e.isBoolean()) { + return e.asBoolean(); + } else if (e.isInt()) { + return e.asInt(); + } else if (e.isLong()) { + return e.asLong(); + } else if (e.isBigInteger()) { + return e.bigIntegerValue(); + } else if (e.isDouble()) { + return e.doubleValue(); + } else if (e.isFloat()) { + return e.floatValue(); + } else if (e.isBigDecimal()) { + return e.decimalValue(); + } else if (e.isNull()) { + return null; + } + } + return o; + } + + @Override + public boolean isArray(Object obj) { + return (obj instanceof ArrayNode || obj instanceof List); + } + + @Override + public Object getArrayIndex(Object obj, int idx) { + return toJsonArray(obj).get(idx); + } + + @Override + public void setArrayIndex(Object array, int index, Object newValue) { + if (!isArray(array)) { + throw new UnsupportedOperationException(); + } else { + ArrayNode arrayNode = toJsonArray(array); + if (index == arrayNode.size()) { + arrayNode.add(createJsonElement(newValue)); + } else { + arrayNode.set(index, createJsonElement(newValue)); + } + } + } + + @Override + public Object getMapValue(Object obj, String key) { + ObjectNode jsonObject = toJsonObject(obj); + Object o = jsonObject.get(key); + if (!jsonObject.has(key)) { + return UNDEFINED; + } else { + return o; + } + } + + @Override + public void setProperty(Object obj, Object key, Object value) { + // jlolling: Bug: #211 avoid create cloned nodes + if (isMap(obj)) { + setValueInObjectNode((ObjectNode) obj, key, value); + } else { + ArrayNode array = (ArrayNode) obj; + int index; + if (key != null) { + index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); + } else { + index = array.size(); + } + if (index == array.size()) { + array.add(createJsonElement(value)); + } else { + array.set(index, createJsonElement(value)); + } + } + } + + public void removeProperty(Object obj, Object key) { + if (isMap(obj)) { + toJsonObject(obj).remove(key.toString()); + } else { + ArrayNode array = toJsonArray(obj); + int index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); + array.remove(index); + } + } + + @Override + public boolean isMap(Object obj) { + return (obj instanceof ObjectNode); + } + + @Override + public Collection getPropertyKeys(Object obj) { + return toJsonObject(obj).propertyNames(); + } + + @Override + public int length(Object obj) { + if (isArray(obj)) { + return toJsonArray(obj).size(); + } else if (isMap(obj)) { + return toJsonObject(obj).size(); + } else { + if (obj instanceof StringNode) { + StringNode element = (StringNode) obj; + return element.size(); + } + } + throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName() : "null")); + } + + @Override + public Iterable toIterable(Object obj) { + ArrayNode arr = toJsonArray(obj); + Iterator iterator = arr.iterator(); + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Object next() { + return unwrap(iterator.next()); + } + }; + } + }; + } + + private JsonNode createJsonElement(Object o) { + if (o != null) { + // jlolling: avoid creating a cloned node: bug #211 + if (o instanceof JsonNode) { + return (JsonNode) o; + } else { + return objectMapper.valueToTree(o); + } + } else { + return null; + } + } + + private ArrayNode toJsonArray(Object o) { + return (ArrayNode) o; + } + + private ObjectNode toJsonObject(Object o) { + return (ObjectNode) o; + } + + private void setValueInObjectNode(ObjectNode objectNode, Object key, Object value) { + // jlolling: necessary to avoid deprecated methods and to avoid creating a cloned node. Bug: #211 + if (value instanceof JsonNode) { + objectNode.set(key.toString(), (JsonNode) value); + } else if (value instanceof String) { + objectNode.put(key.toString(), (String) value); + } else if (value instanceof Integer) { + objectNode.put(key.toString(), (Integer) value); + } else if (value instanceof Long) { + objectNode.put(key.toString(), (Long) value); + } else if (value instanceof Short) { + objectNode.put(key.toString(), (Short) value); + } else if (value instanceof BigInteger) { + objectNode.put(key.toString(), (BigInteger) value); + } else if (value instanceof Double) { + objectNode.put(key.toString(), (Double) value); + } else if (value instanceof Float) { + objectNode.put(key.toString(), (Float) value); + } else if (value instanceof BigDecimal) { + objectNode.put(key.toString(), (BigDecimal) value); + } else if (value instanceof Boolean) { + objectNode.put(key.toString(), (Boolean) value); + } else if (value instanceof byte[]) { + objectNode.put(key.toString(), (byte[]) value); + } else if (value == null) { + objectNode.set(key.toString(), null); // this will create a null-node + } else { + objectNode.set(key.toString(), createJsonElement(value)); + } + } + +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonProvider.java new file mode 100644 index 000000000..d004b8fb2 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonProvider.java @@ -0,0 +1,119 @@ +/* + * Copyright 2011 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jayway.jsonpath.spi.json; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; + +import com.jayway.jsonpath.InvalidJsonException; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; + +public class Jackson3JsonProvider extends AbstractJsonProvider { + + private static final ObjectMapper defaultObjectMapper = new ObjectMapper(); + private static final ObjectReader defaultObjectReader = defaultObjectMapper.reader().forType(Object.class); + + protected ObjectMapper objectMapper; + protected ObjectReader objectReader; + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + /** + * Initialize the JacksonProvider with the default ObjectMapper and ObjectReader + */ + public Jackson3JsonProvider() { + this(defaultObjectMapper, defaultObjectReader); + } + + /** + * Initialize the JacksonProvider with a custom ObjectMapper. + * + * @param objectMapper the ObjectMapper to use + */ + public Jackson3JsonProvider(ObjectMapper objectMapper) { + this(objectMapper, objectMapper.reader().forType(Object.class)); + } + + /** + * Initialize the JacksonProvider with a custom ObjectMapper and ObjectReader. + * + * @param objectMapper the ObjectMapper to use + * @param objectReader the ObjectReader to use + */ + public Jackson3JsonProvider(ObjectMapper objectMapper, ObjectReader objectReader) { + this.objectMapper = objectMapper; + this.objectReader = objectReader; + } + + @Override + public Object parse(String json) throws InvalidJsonException { + try { + return objectReader.readValue(json); + } catch (JacksonException e) { + throw new InvalidJsonException(e, json); + } + } + + @Override + public Object parse(byte[] json) throws InvalidJsonException { + try { + return objectReader.readValue(json); + } catch (JacksonException e) { + throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8)); + } + } + + @Override + public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException { + try { + return objectReader.readValue(new InputStreamReader(jsonStream, charset)); + } catch (IOException e) { + throw new InvalidJsonException(e); + } + } + + @Override + public String toJson(Object obj) { + StringWriter writer = new StringWriter(); + try { + objectMapper.writeValue(writer, obj); + writer.flush(); + writer.close(); + return writer.getBuffer().toString(); + } catch (IOException e) { + throw new InvalidJsonException(e); + } + } + + @Override + public List createArray() { + return new LinkedList(); + } + + @Override + public Object createMap() { + return new LinkedHashMap(); + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/mapper/Jackson3MappingProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/mapper/Jackson3MappingProvider.java new file mode 100644 index 000000000..115576610 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/mapper/Jackson3MappingProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright 2011 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jayway.jsonpath.spi.mapper; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.TypeRef; +import tools.jackson.databind.JavaType; +import tools.jackson.databind.ObjectMapper; + +public class Jackson3MappingProvider implements MappingProvider { + + private final ObjectMapper objectMapper; + + public Jackson3MappingProvider() { + this(new ObjectMapper()); + } + + public Jackson3MappingProvider(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public T map(Object source, Class targetType, Configuration configuration) { + if (source == null) { + return null; + } + try { + return objectMapper.convertValue(source, targetType); + } catch (Exception e) { + throw new MappingException(e); + } + + } + + @Override + public T map(Object source, final TypeRef targetType, Configuration configuration) { + if (source == null) { + return null; + } + JavaType type = objectMapper.getTypeFactory().constructType(targetType.getType()); + + try { + return (T) objectMapper.convertValue(source, type); + } catch (Exception e) { + throw new MappingException(e); + } + + } +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java b/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java index eac44cdb1..041afdeb4 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/BaseTest.java @@ -5,12 +5,15 @@ import com.jayway.jsonpath.spi.json.GsonJsonProvider; import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider; import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import com.jayway.jsonpath.spi.json.Jackson3JsonNodeJsonProvider; +import com.jayway.jsonpath.spi.json.Jackson3JsonProvider; import com.jayway.jsonpath.spi.json.JakartaJsonProvider; import com.jayway.jsonpath.spi.json.JettisonProvider; import com.jayway.jsonpath.spi.json.JsonOrgJsonProvider; import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider; import com.jayway.jsonpath.spi.json.TapestryJsonProvider; import com.jayway.jsonpath.spi.mapper.GsonMappingProvider; +import com.jayway.jsonpath.spi.mapper.Jackson3MappingProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.JakartaMappingProvider; import com.jayway.jsonpath.spi.mapper.JsonOrgMappingProvider; @@ -45,6 +48,18 @@ public class BaseTest { .jsonProvider(new JacksonJsonNodeJsonProvider()) .build(); + public static final Configuration JACKSON3_CONFIGURATION = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonProvider()) + .build(); + + public static final Configuration JACKSON3_JSON_NODE_CONFIGURATION = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonNodeJsonProvider()) + .build(); + public static final Configuration JETTISON_CONFIGURATION = Configuration .builder() .jsonProvider(new JettisonProvider()) diff --git a/json-path/src/test/java/com/jayway/jsonpath/Configurations.java b/json-path/src/test/java/com/jayway/jsonpath/Configurations.java index 537d2be0e..bef955ff4 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/Configurations.java +++ b/json-path/src/test/java/com/jayway/jsonpath/Configurations.java @@ -1,12 +1,15 @@ package com.jayway.jsonpath; import com.jayway.jsonpath.spi.json.GsonJsonProvider; +import com.jayway.jsonpath.spi.json.Jackson3JsonNodeJsonProvider; +import com.jayway.jsonpath.spi.json.Jackson3JsonProvider; import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider; import com.jayway.jsonpath.spi.json.JacksonJsonProvider; import com.jayway.jsonpath.spi.json.JakartaJsonProvider; import com.jayway.jsonpath.spi.json.JsonOrgJsonProvider; import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider; import com.jayway.jsonpath.spi.mapper.GsonMappingProvider; +import com.jayway.jsonpath.spi.mapper.Jackson3MappingProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.JakartaMappingProvider; import com.jayway.jsonpath.spi.mapper.JsonOrgMappingProvider; @@ -40,6 +43,18 @@ public class Configurations { .jsonProvider(new JacksonJsonNodeJsonProvider()) .build(); + public static final Configuration JACKSON3_CONFIGURATION = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonProvider()) + .build(); + + public static final Configuration JACKSON3_JSON_NODE_CONFIGURATION = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonNodeJsonProvider()) + .build(); + public static final Configuration JSON_SMART_CONFIGURATION = Configuration .builder() .mappingProvider(new JsonSmartMappingProvider()) @@ -58,6 +73,8 @@ public static Iterable configurations() { ,GSON_CONFIGURATION ,JACKSON_CONFIGURATION ,JACKSON_JSON_NODE_CONFIGURATION + ,JACKSON3_CONFIGURATION + ,JACKSON3_JSON_NODE_CONFIGURATION ,JSON_ORG_CONFIGURATION ,JAKARTA_CONFIGURATION ); @@ -68,6 +85,8 @@ public static Iterable objectMappingConfigurations() { GSON_CONFIGURATION ,JACKSON_CONFIGURATION ,JACKSON_JSON_NODE_CONFIGURATION + ,JACKSON3_CONFIGURATION + ,JACKSON3_JSON_NODE_CONFIGURATION ,JAKARTA_CONFIGURATION ); } diff --git a/json-path/src/test/java/com/jayway/jsonpath/Jackson3JsonNodeJsonProviderMapperSupportTest.java b/json-path/src/test/java/com/jayway/jsonpath/Jackson3JsonNodeJsonProviderMapperSupportTest.java new file mode 100644 index 000000000..96720d139 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/Jackson3JsonNodeJsonProviderMapperSupportTest.java @@ -0,0 +1,143 @@ +package com.jayway.jsonpath; + +import java.util.Arrays; +import java.util.List; + +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.StringNode; +import com.jayway.jsonpath.spi.json.Jackson3JsonNodeJsonProvider; +import com.jayway.jsonpath.spi.mapper.Jackson3MappingProvider; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class Jackson3JsonNodeJsonProviderMapperSupportTest { + + @ParameterizedTest + @MethodSource("testDataSource") + public void mapMethod_withJacksonJsonNodeJsonProvider_shouldUsingJsonNodeForMappingValues(TestData testData) { + DocumentContext testJsonDocumentContext = cloneDocumentContext(testData.jsonDocumentContext); + + testJsonDocumentContext.map(testData.jsonPath, (value, config) -> { + assertThat(value.getClass()).isEqualTo(testData.expectedJsonValueNodeType); + return testData.newJsonValue; + }); + assertThat((JsonNode) testJsonDocumentContext.json()) + .isEqualTo(testData.expectedUpdatedJsonDocument); + } + + + @ParameterizedTest + @MethodSource("testDataSource") + public void readMethod_withJacksonJsonNodeJsonProvider_shouldReturnJsonNode(TestData testData) { + DocumentContext testJsonDocumentContext = cloneDocumentContext(testData.jsonDocumentContext); + + final JsonNode actualJsonValue = testJsonDocumentContext.read(testData.jsonPath); + assertThat(actualJsonValue).isEqualTo(testData.expectedJsonValue); + } + + + @ParameterizedTest + @MethodSource("testDataSource") + public void setMethod_withJacksonJsonNodeJsonProvider_shouldAcceptJsonNode(TestData testData) { + DocumentContext testJsonDocumentContext = cloneDocumentContext(testData.jsonDocumentContext); + + testJsonDocumentContext.set(testData.jsonPath, testData.newJsonValue); + assertThat((JsonNode) testJsonDocumentContext.json()) + .isEqualTo(testData.expectedUpdatedJsonDocument); + } + + private static class TestData { + + public final DocumentContext jsonDocumentContext; + public final String jsonPath; + public final JsonNode newJsonValue; + public final JsonNode expectedJsonValue; + public final Class expectedJsonValueNodeType; + public final JsonNode expectedUpdatedJsonDocument; + + public TestData( + DocumentContext jsonDocumentContext, + String jsonPath, + JsonNode newJsonValue, + JsonNode expectedJsonValue, + Class expectedJsonValueNodeType, + JsonNode expectedUpdatedJsonDocument) { + this.jsonDocumentContext = jsonDocumentContext; + this.jsonPath = jsonPath; + this.newJsonValue = newJsonValue; + this.expectedJsonValue = expectedJsonValue; + this.expectedJsonValueNodeType = expectedJsonValueNodeType; + this.expectedUpdatedJsonDocument = expectedUpdatedJsonDocument; + } + } + + + public static List testDataSource() throws Exception { + final Configuration configuration = Configuration.builder() + .jsonProvider(new Jackson3JsonNodeJsonProvider()) + .mappingProvider(new Jackson3MappingProvider()) + .build(); + final ParseContext parseContext = JsonPath.using(configuration); + final ObjectMapper objectMapper = new ObjectMapper(); + + return Arrays.asList( + // Single value JSON path + new TestData( + parseContext.parse("{" + + " \"attr1\": \"val1\"," + + " \"attr2\": \"val2\"" + + "}"), + "$.attr1", + objectMapper.readTree("{\"attr1\": \"val1\"}"), + objectMapper.readTree("\"val1\""), + StringNode.class, + objectMapper.readTree("{" + + " \"attr1\": {\"attr1\": \"val1\"}," + + " \"attr2\": \"val2\"" + + "}")), + // Multi-value JSON path + new TestData( + parseContext.parse("{" + + " \"attr1\": [\"val1\", \"val2\"]," + + " \"attr2\": \"val2\"" + + "}"), + "$.attr1[*]", + objectMapper.readTree("{\"attr1\": \"val1\"}"), + objectMapper.readTree("[\"val1\", \"val2\"]"), + StringNode.class, + objectMapper.readTree("{" + + " \"attr1\": [{\"attr1\": \"val1\"}, {\"attr1\": \"val1\"}]," + + " \"attr2\": \"val2\"" + + "}")), + // Multi-value object JSON path + new TestData( + parseContext.parse("{" + + " \"attr1\": [" + + " {\"inAttr1\": \"val1a\", \"inAttr2\": \"val2a\"}," + + " {\"inAttr1\": \"val1a\", \"inAttr2\": \"val2b\"}," + + " {\"inAttr1\": \"val1b\", \"inAttr2\": \"val2c\"}" + + " ]," + + " \"attr2\": \"val2\"" + + "}"), + "$.attr1.[?(@.inAttr1 == \"val1a\")].inAttr2", + objectMapper.readTree("{\"attr1\": \"val1\"}"), + objectMapper.readTree("[\"val2a\", \"val2b\"]"), + StringNode.class, + objectMapper.readTree("{" + + " \"attr1\": [" + + " {\"inAttr1\": \"val1a\", \"inAttr2\": {\"attr1\": \"val1\"}}," + + " {\"inAttr1\": \"val1a\", \"inAttr2\": {\"attr1\": \"val1\"}}," + + " {\"inAttr1\": \"val1b\", \"inAttr2\": \"val2c\"}" + + " ]," + + " \"attr2\": \"val2\"" + + "}")) + ); + } + + private static DocumentContext cloneDocumentContext(DocumentContext documentContext) { + return JsonPath.using(documentContext.configuration()).parse(documentContext.jsonString()); + } +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/Jackson3JsonNodeJsonProviderTest.java b/json-path/src/test/java/com/jayway/jsonpath/Jackson3JsonNodeJsonProviderTest.java new file mode 100644 index 000000000..06b595a54 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/Jackson3JsonNodeJsonProviderTest.java @@ -0,0 +1,284 @@ +package com.jayway.jsonpath; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; +import com.jayway.jsonpath.spi.json.Jackson3JsonNodeJsonProvider; +import com.jayway.jsonpath.spi.mapper.Jackson3MappingProvider; +import com.jayway.jsonpath.spi.mapper.MappingException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static com.jayway.jsonpath.JsonPath.using; +import static org.assertj.core.api.Assertions.assertThat; + +public class Jackson3JsonNodeJsonProviderTest extends BaseTest { + + private static final String JSON = + "[" + + "{\n" + + " \"foo\" : \"foo0\",\n" + + " \"bar\" : 0,\n" + + " \"baz\" : true,\n" + + " \"gen\" : {\"eric\" : \"yepp\"}" + + "}," + + "{\n" + + " \"foo\" : \"foo1\",\n" + + " \"bar\" : 1,\n" + + " \"baz\" : true,\n" + + " \"gen\" : {\"eric\" : \"yepp\"}" + + "}," + + "{\n" + + " \"foo\" : \"foo2\",\n" + + " \"bar\" : 2,\n" + + " \"baz\" : true,\n" + + " \"gen\" : {\"eric\" : \"yepp\"}" + + "}" + + "]"; + + @Test + public void json_can_be_parsed() { + ObjectNode node = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$"); + assertThat(node.get("string-property").asText()).isEqualTo("string-value"); + } + + @Test + public void bytes_json_can_be_parsed() { + ObjectNode node = using(JACKSON3_JSON_NODE_CONFIGURATION).parseUtf8(JSON_DOCUMENT.getBytes(StandardCharsets.UTF_8)) + .read("$"); + assertThat(node.get("string-property").asText()).isEqualTo("string-value"); + } + + @Test + public void always_return_same_object() { // Test because of Bug #211 + DocumentContext context = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT); + ObjectNode node1 = context.read("$"); + ObjectNode child1 = new ObjectNode(JsonNodeFactory.instance); + child1.put("name", "test"); + context.put("$", "child", child1); + ObjectNode node2 = context.read("$"); + ObjectNode child2 = context.read("$.child"); + + assertThat(node1).isSameAs(node2); + assertThat(child1).isSameAs(child2); + } + + @Test + public void strings_are_unwrapped() { + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.string-property"); + String unwrapped = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.string-property", String.class); + + assertThat(unwrapped).isEqualTo("string-value"); + assertThat(unwrapped).isEqualTo(node.asText()); + } + + @Test + public void ints_are_unwrapped() { + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property"); + int unwrapped = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.int-max-property", int.class); + assertThat(unwrapped).isEqualTo(Integer.MAX_VALUE); + assertThat(unwrapped).isEqualTo(node.asInt()); + } + + @Test + public void longs_are_unwrapped() { + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.long-max-property"); + long unwrapped = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.long-max-property", long.class); + + assertThat(unwrapped).isEqualTo(Long.MAX_VALUE); + assertThat(unwrapped).isEqualTo(node.asLong()); + } + + @Test + public void list_of_numbers() { + ArrayNode objs = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT).read("$.store.book[*].display-price"); + + assertThat(objs.get(0).asDouble()).isEqualTo(8.95D); + assertThat(objs.get(1).asDouble()).isEqualTo(12.99D); + assertThat(objs.get(2).asDouble()).isEqualTo(8.99D); + assertThat(objs.get(3).asDouble()).isEqualTo(22.99D); + } + + ObjectMapper objectMapperDecimal = JsonMapper.builder() + .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS).build(); + Configuration JACKSON3_JSON_NODE_CONFIGURATION_DECIMAL = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonNodeJsonProvider(objectMapperDecimal)) + .build(); + + @Test + public void bigdecimals_are_unwrapped() { + final BigDecimal bd = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.valueOf(10.5)); + final String json = "{\"bd-property\" : " + bd.toString() + "}"; + + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION_DECIMAL).parse(json).read("$.bd-property"); + BigDecimal val = using(JACKSON3_JSON_NODE_CONFIGURATION_DECIMAL).parse(json).read("$.bd-property", BigDecimal.class); + + assertThat(node.isBigDecimal()).isTrue(); + assertThat(val).isEqualTo(bd); + assertThat(val).isEqualTo(node.decimalValue()); + } + + @Test + public void small_bigdecimals_are_unwrapped() { + final BigDecimal bd = BigDecimal.valueOf(10.5); + final String json = "{\"bd-property\" : " + bd.toString() + "}"; + + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION_DECIMAL).parse(json).read("$.bd-property"); + BigDecimal val = using(JACKSON3_JSON_NODE_CONFIGURATION_DECIMAL).parse(json).read("$.bd-property", BigDecimal.class); + + assertThat(node.isBigDecimal()).isTrue(); + assertThat(val).isEqualTo(bd); + assertThat(val).isEqualTo(node.decimalValue()); + } + + ObjectMapper objectMapperBigInteger = JsonMapper.builder() + .enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS).build();; + Configuration JACKSON3_JSON_NODE_CONFIGURATION_Big_Integer = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonNodeJsonProvider(objectMapperBigInteger)) + .build(); + + @Test + public void bigintegers_are_unwrapped() { + final BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TEN); + final String json = "{\"bi-property\" : " + bi.toString() + "}"; + + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION_Big_Integer).parse(json).read("$.bi-property"); + BigInteger val = using(JACKSON3_JSON_NODE_CONFIGURATION_Big_Integer).parse(json).read("$.bi-property", BigInteger.class); + + assertThat(node.isBigInteger()).isTrue(); + assertThat(val).isEqualTo(bi); + assertThat(val).isEqualTo(node.bigIntegerValue()); + } + + @Test + public void small_bigintegers_are_unwrapped() { + final BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE); + final String json = "{\"bi-property\" : " + bi.toString() + "}"; + + JsonNode node = using(JACKSON3_JSON_NODE_CONFIGURATION_Big_Integer).parse(json).read("$.bi-property"); + BigInteger val = using(JACKSON3_JSON_NODE_CONFIGURATION_Big_Integer).parse(json).read("$.bi-property", BigInteger.class); + + assertThat(node.isBigInteger()).isTrue(); + assertThat(val).isEqualTo(bi); + assertThat(val).isEqualTo(node.bigIntegerValue()); + } + + @Test + public void test_type_ref() throws IOException { + TypeRef>> typeRef = new TypeRef>>() {}; + + List> list = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON).read("$", typeRef); + + assertThat(list.get(0).gen.eric).isEqualTo("yepp"); + } + + @Test + public void test_type_ref_fail() throws IOException { + TypeRef>> typeRef = new TypeRef>>() {}; + + Assertions.assertThrows(MappingException.class, () -> using(JACKSON3_JSON_NODE_CONFIGURATION).parse(JSON).read("$", typeRef)); + } + + @Test + public void mapPropertyWithPOJO() { + String someJson = "" + + "{\n" + + " \"a\": \"a\",\n" + + " \"b\": \"b\"\n" + + "}"; + ObjectMapper om = JsonMapper.builder() + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS).build(); + Configuration c = Configuration + .builder() + .mappingProvider(new Jackson3MappingProvider()) + .jsonProvider(new Jackson3JsonNodeJsonProvider(om)) + .build(); + DocumentContext context = JsonPath.using(c).parse(someJson); + String someJsonStr = context.jsonString(); + DocumentContext altered = context.map("$['a', 'b', 'c']", new MapFunction() { + @Override + public Object map(Object currentValue, Configuration configuration) { + return currentValue; + } + }); + assertThat(altered.jsonString()).isEqualTo(someJsonStr); + } + + @Test + // https://github.com/json-path/JsonPath/issues/364 + public void setPropertyWithPOJO() { + DocumentContext context = JsonPath.using(JACKSON3_JSON_NODE_CONFIGURATION).parse("{}"); + UUID uuid = UUID.randomUUID(); + context.put("$", "data", new Data(uuid)); + String id = context.read("$.data.id", String.class); + assertThat(id).isEqualTo(uuid.toString()); + } + // https://github.com/json-path/JsonPath/issues/366 + public void empty_array_check_works() throws IOException { + String json = "[" + + " {" + + " \"name\": \"a\"," + + " \"groups\": [{" + + " \"type\": \"phase\"," + + " \"name\": \"alpha\"" + + " }, {" + + " \"type\": \"not_phase\"," + + " \"name\": \"beta\"" + + " }]" + + " }, {" + + " \"name\": \"b\"," + + " \"groups\": [{" + + " \"type\": \"phase\"," + + " \"name\": \"beta\"" + + " }, {" + + " \"type\": \"not_phase\"," + + " \"name\": \"alpha\"" + + " }]" + + " }" + + "]"; + ArrayNode node = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(json).read("$[?(@.groups[?(@.type == 'phase' && @.name == 'alpha')] empty false)]"); + assertThat(node.size()).isEqualTo(1); + assertThat(node.get(0).get("name").asText()).isEqualTo("a"); + } + + public static class FooBarBaz { + public T gen; + public String foo; + public Long bar; + public boolean baz; + } + + + public static class Gen { + public String eric; + } + + public static final class Data { + @JsonProperty("id") + UUID id; + + @JsonCreator + Data(@JsonProperty("id") final UUID id) { + this.id = id; + } + } + +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/Jackson3Test.java b/json-path/src/test/java/com/jayway/jsonpath/Jackson3Test.java new file mode 100644 index 000000000..28bd3beac --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/Jackson3Test.java @@ -0,0 +1,69 @@ +package com.jayway.jsonpath; + +import java.util.Date; + +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +public class Jackson3Test extends BaseTest { + + @Test + public void an_object_can_be_mapped_to_pojo() { + + String json = "{\n" + + " \"foo\" : \"foo\",\n" + + " \"bar\" : 10,\n" + + " \"baz\" : true\n" + + "}"; + + + FooBarBaz fooBarBaz = JsonPath.using(JACKSON3_CONFIGURATION).parse(json).read("$", FooBarBaz.class); + + assertThat(fooBarBaz.foo).isEqualTo("foo"); + assertThat(fooBarBaz.bar).isEqualTo(10L); + assertThat(fooBarBaz.baz).isEqualTo(true); + + fooBarBaz = JsonPath.using(JACKSON3_CONFIGURATION).parseUtf8(json.getBytes(UTF_8)) + .read("$", FooBarBaz.class); + + assertThat(fooBarBaz.foo).isEqualTo("foo"); + assertThat(fooBarBaz.bar).isEqualTo(10L); + assertThat(fooBarBaz.baz).isEqualTo(true); + } + + public static class FooBarBaz { + public String foo; + public Long bar; + public boolean baz; + } + + @Test + public void jackson_converts_dates() { + + Date now = new Date(); + + Object json = singletonMap("date_as_long", now.getTime()); + + Date date = JsonPath.using(JACKSON3_CONFIGURATION).parse(json).read("$['date_as_long']", Date.class); + + assertThat(date).isEqualTo(now); + } + + @Test + // https://github.com/jayway/JsonPath/issues/275 + public void single_quotes_work_with_in_filter() { + final String jsonArray = "[{\"foo\": \"bar\"}, {\"foo\": \"baz\"}]"; + final Object readFromSingleQuote = JsonPath.using(JACKSON3_CONFIGURATION).parse(jsonArray).read("$.[?(@.foo in ['bar'])].foo"); + final Object readFromDoubleQuote = JsonPath.using(JACKSON3_CONFIGURATION).parse(jsonArray).read("$.[?(@.foo in [\"bar\"])].foo"); + assertThat(readFromSingleQuote).isEqualTo(readFromDoubleQuote); + final Object readFromSingleQuoteBytes = JsonPath.using(JACKSON3_CONFIGURATION).parseUtf8(jsonArray.getBytes(UTF_8)) + .read("$.[?(@.foo in ['bar'])].foo"); + final Object readFromDoubleQuoteBytes = JsonPath.using(JACKSON3_CONFIGURATION).parseUtf8(jsonArray.getBytes(UTF_8)) + .read("$.[?(@.foo in [\"bar\"])].foo"); + assertThat(readFromSingleQuoteBytes).isEqualTo(readFromDoubleQuoteBytes); + } + +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java b/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java index f99bfca13..df4c877bc 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java @@ -60,6 +60,42 @@ public void testJsonPathQuotesJacksonJsonNode() { assertEquals(doubleQuoteInResult, singleQuoteInResult); } + @Test + public void testJsonPathQuotesJackson3() { + final Configuration jackson3 = Configuration.builder().jsonProvider(new Jackson3JsonProvider()).mappingProvider(new Jackson3MappingProvider()).build(); + final DocumentContext ctx = JsonPath.using(jackson3).parse(JSON); + + final List doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER); + assertEquals(Lists.newArrayList("bar"), doubleQuoteEqualsResult); + + final List singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER); + assertEquals(doubleQuoteEqualsResult, singleQuoteEqualsResult); + + final List doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER); + assertEquals(doubleQuoteInResult, doubleQuoteEqualsResult); + + final List singleQuoteInResult = ctx.read(SINGLE_QUOTES_IN_FILTER); + assertEquals(doubleQuoteInResult, singleQuoteInResult); + } + + @Test + public void testJsonPathQuotesJackson3JsonNode() { + final Configuration jackson3JsonNode = Configuration.builder().jsonProvider(new Jackson3JsonNodeJsonProvider()).mappingProvider(new Jackson3MappingProvider()).build(); + final DocumentContext ctx = JsonPath.using(jackson3JsonNode).parse(JSON); + + final tools.jackson.databind.node.ArrayNode doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER); + assertEquals("bar", doubleQuoteEqualsResult.get(0).asText()); + + final tools.jackson.databind.node.ArrayNode singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER); + assertEquals(doubleQuoteEqualsResult, singleQuoteEqualsResult); + + final tools.jackson.databind.node.ArrayNode doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER); + assertEquals(doubleQuoteInResult, doubleQuoteEqualsResult); + + final tools.jackson.databind.node.ArrayNode singleQuoteInResult = ctx.read(SINGLE_QUOTES_IN_FILTER); + assertEquals(doubleQuoteInResult, singleQuoteInResult); + } + @Test public void testJsonPathQuotesGson() { final Configuration gson = Configuration.builder().jsonProvider(new GsonJsonProvider()).mappingProvider(new GsonMappingProvider()).build(); diff --git a/json-path/src/test/java/com/jayway/jsonpath/TestSuppressExceptions.java b/json-path/src/test/java/com/jayway/jsonpath/TestSuppressExceptions.java index 5715c264e..174d9c2e1 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/TestSuppressExceptions.java +++ b/json-path/src/test/java/com/jayway/jsonpath/TestSuppressExceptions.java @@ -1,6 +1,8 @@ package com.jayway.jsonpath; +import com.jayway.jsonpath.spi.json.Jackson3JsonProvider; import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import com.jayway.jsonpath.spi.mapper.Jackson3MappingProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import org.junit.jupiter.api.Test; @@ -30,6 +32,27 @@ public void testSuppressExceptionsIsRespectedPath() { .build()); String json = "{}"; + List result = parseContext.parse(json).read(JsonPath.compile("$.missing")); + assertThat(result).isEmpty(); + } + @Test + public void testSuppressExceptionsIsRespectedJackson3() { + ParseContext parseContext = JsonPath.using( + new Configuration.ConfigurationBuilder().jsonProvider(new Jackson3JsonProvider()) + .mappingProvider(new Jackson3MappingProvider()).options(Option.SUPPRESS_EXCEPTIONS) + .build()); + String json = "{}"; + assertNull(parseContext.parse(json).read(JsonPath.compile("$.missing"))); + } + + @Test + public void testSuppressExceptionsIsRespectedPathJackson3() { + ParseContext parseContext = JsonPath.using( + new Configuration.ConfigurationBuilder().jsonProvider(new Jackson3JsonProvider()) + .mappingProvider(new Jackson3MappingProvider()).options(Option.SUPPRESS_EXCEPTIONS, Option.AS_PATH_LIST) + .build()); + String json = "{}"; + List result = parseContext.parse(json).read(JsonPath.compile("$.missing")); assertThat(result).isEmpty(); } diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java index 7beeb4a35..67e90790d 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java @@ -972,7 +972,27 @@ public void issue_170() { List list = context.read("$.array", List.class); assertThat(list).containsExactly(null, 1, null); + } + + @Test + public void issue_170_jackson3() { + + String json = "{\n" + + " \"array\": [\n" + + " 0,\n" + + " 1,\n" + + " 2\n" + + " ]\n" + + "}"; + + DocumentContext context = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(json); + context = context.set("$.array[0]", null); + context = context.set("$.array[2]", null); + + List list = context.read("$.array", List.class); + + assertThat(list).containsExactly(null, 1, null); } @Test @@ -993,6 +1013,24 @@ public void issue_171() { assertThat(objectNode.get("can't delete").isNull()); } + @Test + public void issue_171_jackson3() { + + String json = "{\n" + + " \"can delete\": \"this\",\n" + + " \"can't delete\": \"this\"\n" + + "}"; + + DocumentContext context = using(JACKSON3_JSON_NODE_CONFIGURATION).parse(json); + context.set("$.['can delete']", null); + context.set("$.['can\\'t delete']", null); + + tools.jackson.databind.node.ObjectNode objectNode = context.read("$"); + + assertThat(objectNode.get("can delete").isNull()); + assertThat(objectNode.get("can't delete").isNull()); + } + @Test public void issue_309() {