Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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/)
Expand Down Expand Up @@ -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/)
Expand Down
24 changes: 13 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down
1 change: 1 addition & 0 deletions json-path/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> 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<Object>() {
@Override
public Iterator<Object> iterator() {
return new Iterator<Object>() {
@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));
}
}

}
Loading