Skip to content

Commit 5a7f29e

Browse files
authored
adding jackson3 provider (#1066) (#1067)
* adding jackson3 provider * change to minimal java version 17
1 parent b6c60b3 commit 5a7f29e

17 files changed

Lines changed: 1124 additions & 18 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
java: [ 8, 11, 17, 21 ]
13+
java: [ 17, 21 ]
1414
fail-fast: false
1515
max-parallel: 4
1616
name: JDK ${{ matrix.java }}

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ language: java
33
sudo: false
44

55
jdk:
6-
- openjdk8
6+
- openjdk17
77

88
cache:
99
directories:

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ String json = "{\"date_as_long\" : 1411455611975}";
233233
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
234234
```
235235

236-
If you configure JsonPath to use `JacksonMappingProvider`, `GsonMappingProvider`, or `JakartaJsonProvider` you can even
236+
If you configure JsonPath to use `JacksonMappingProvider`, `Jackson3MappingProvider`, `GsonMappingProvider`, or `JakartaJsonProvider` you can even
237237
map your JsonPath output directly into POJO's.
238238

239239
```java
@@ -434,6 +434,8 @@ JsonPath is shipped with five different JsonProviders:
434434
* [JsonSmartJsonProvider](https://github.com/netplex/json-smart-v2) (default)
435435
* [JacksonJsonProvider](https://github.com/FasterXML/jackson)
436436
* [JacksonJsonNodeJsonProvider](https://github.com/FasterXML/jackson)
437+
* [JacksonJson3Provider](https://github.com/FasterXML/jackson)
438+
* [JacksonJson3NodeJsonProvider](https://github.com/FasterXML/jackson)
437439
* [GsonJsonProvider](https://code.google.com/p/google-gson/)
438440
* [JsonOrgJsonProvider](https://github.com/stleary/JSON-java)
439441
* [JakartaJsonProvider](https://javaee.github.io/jsonp/)
@@ -464,7 +466,7 @@ Configuration.setDefaults(new Configuration.Defaults() {
464466
});
465467
```
466468

467-
Note that the JacksonJsonProvider requires `com.fasterxml.jackson.core:jackson-databind:2.4.5` and the GsonJsonProvider
469+
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
468470
requires `com.google.code.gson:gson:2.3.1` on your classpath.
469471

470472
Both of Jakarta EE 9 [JSON-P (JSR-342)](https://javaee.github.io/jsonp/) and [JSON-B (JSR-367)](http://json-b.net/)

build.gradle

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@ buildscript {
1212

1313
ext {
1414
libs = [
15-
jsonSmart : 'net.minidev:json-smart:2.6.0',
16-
slf4jApi : 'org.slf4j:slf4j-api:2.0.17',
17-
gson : 'com.google.code.gson:gson:2.13.2',
18-
hamcrest : 'org.hamcrest:hamcrest:3.0',
19-
jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.19.2',
20-
jettison : 'org.codehaus.jettison:jettison:1.5.4',
21-
jsonOrg : 'org.json:json:20250517',
22-
tapestryJson : 'org.apache.tapestry:tapestry-json:5.9.0',
23-
jakartaJsonP : 'jakarta.json:jakarta.json-api:2.1.3',
24-
jakartaJsonB : 'jakarta.json.bind:jakarta.json.bind-api:2.0.0',
15+
jsonSmart : 'net.minidev:json-smart:2.6.0',
16+
slf4jApi : 'org.slf4j:slf4j-api:2.0.17',
17+
gson : 'com.google.code.gson:gson:2.13.2',
18+
hamcrest : 'org.hamcrest:hamcrest:3.0',
19+
jacksonDatabind : 'com.fasterxml.jackson.core:jackson-databind:2.20.1',
20+
jacksonDatabind3: 'tools.jackson.core:jackson-databind:3.0.3',
21+
jettison : 'org.codehaus.jettison:jettison:1.5.4',
22+
jsonOrg : 'org.json:json:20250517',
23+
tapestryJson : 'org.apache.tapestry:tapestry-json:5.9.0',
24+
jakartaJsonP : 'jakarta.json:jakarta.json-api:2.1.3',
25+
jakartaJsonB : 'jakarta.json.bind:jakarta.json.bind-api:2.0.0',
2526

2627
test : [
2728
'commons-io:commons-io:2.20.0',
@@ -33,7 +34,8 @@ ext {
3334
'org.slf4j:slf4j-simple:2.0.17',
3435
'com.google.code.gson:gson:2.13.2',
3536
'org.hamcrest:hamcrest:3.0',
36-
'com.fasterxml.jackson.core:jackson-databind:2.19.2',
37+
'com.fasterxml.jackson.core:jackson-databind:2.20.1',
38+
'tools.jackson.core:jackson-databind:3.0.3',
3739
'org.codehaus.jettison:jettison:1.5.4',
3840
'org.json:json:20250517',
3941
'org.apache.tapestry:tapestry-json:5.9.0',
@@ -62,8 +64,8 @@ subprojects {
6264
apply plugin: 'biz.aQute.bnd.builder'
6365

6466
java {
65-
sourceCompatibility = JavaVersion.VERSION_1_8
66-
targetCompatibility = JavaVersion.VERSION_1_8
67+
sourceCompatibility = JavaVersion.VERSION_17
68+
targetCompatibility = JavaVersion.VERSION_17
6769
}
6870

6971
repositories {

json-path/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies {
1313
implementation libs.jsonSmart
1414
implementation libs.slf4jApi
1515
compileOnly libs.jacksonDatabind // , optional
16+
compileOnly libs.jacksonDatabind3 // , optional
1617
compileOnly libs.gson// , optional
1718
compileOnly libs.jsonOrg// , optional
1819
compileOnly libs.tapestryJson// , optional
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
package com.jayway.jsonpath.spi.json;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.InputStreamReader;
6+
import java.math.BigDecimal;
7+
import java.math.BigInteger;
8+
import java.nio.charset.StandardCharsets;
9+
import java.util.Collection;
10+
import java.util.Iterator;
11+
import java.util.List;
12+
13+
import com.jayway.jsonpath.InvalidJsonException;
14+
import com.jayway.jsonpath.JsonPathException;
15+
import tools.jackson.core.JacksonException;
16+
import tools.jackson.databind.JsonNode;
17+
import tools.jackson.databind.ObjectMapper;
18+
import tools.jackson.databind.node.ArrayNode;
19+
import tools.jackson.databind.node.JsonNodeFactory;
20+
import tools.jackson.databind.node.ObjectNode;
21+
import tools.jackson.databind.node.StringNode;
22+
23+
public class Jackson3JsonNodeJsonProvider extends AbstractJsonProvider {
24+
25+
private static final ObjectMapper defaultObjectMapper = new ObjectMapper();
26+
27+
protected ObjectMapper objectMapper;
28+
29+
public ObjectMapper getObjectMapper() {
30+
return objectMapper;
31+
}
32+
33+
/**
34+
* Initialize the JacksonJsonNodeJsonProvider with the default ObjectMapper and ObjectReader
35+
*/
36+
public Jackson3JsonNodeJsonProvider() {
37+
this(defaultObjectMapper);
38+
}
39+
40+
/**
41+
* Initialize the JacksonJsonNodeJsonProvider with a custom ObjectMapper and ObjectReader.
42+
*
43+
* @param objectMapper the ObjectMapper to use
44+
*/
45+
public Jackson3JsonNodeJsonProvider(ObjectMapper objectMapper) {
46+
this.objectMapper = objectMapper;
47+
}
48+
49+
@Override
50+
public Object parse(String json) throws InvalidJsonException {
51+
try {
52+
return objectMapper.readTree(json);
53+
} catch (JacksonException e) {
54+
throw new InvalidJsonException(e, json);
55+
}
56+
}
57+
58+
@Override
59+
public Object parse(byte[] json) throws InvalidJsonException {
60+
try {
61+
return objectMapper.readTree(json);
62+
} catch (JacksonException e) {
63+
throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8));
64+
}
65+
}
66+
67+
@Override
68+
public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException {
69+
try {
70+
return objectMapper.readTree(new InputStreamReader(jsonStream, charset));
71+
} catch (IOException e) {
72+
throw new InvalidJsonException(e);
73+
}
74+
}
75+
76+
@Override
77+
public String toJson(Object obj) {
78+
if (!(obj instanceof JsonNode)) {
79+
throw new JsonPathException("Not a JSON Node");
80+
}
81+
return obj.toString();
82+
}
83+
84+
@Override
85+
public Object createArray() {
86+
return JsonNodeFactory.instance.arrayNode();
87+
}
88+
89+
@Override
90+
public Object createMap() {
91+
return JsonNodeFactory.instance.objectNode();
92+
}
93+
94+
public Object unwrap(Object o) {
95+
if (o == null) {
96+
return null;
97+
}
98+
if (!(o instanceof JsonNode)) {
99+
return o;
100+
}
101+
102+
JsonNode e = (JsonNode) o;
103+
104+
if (e.isValueNode()) {
105+
106+
if (e.isString()) {
107+
return e.asString();
108+
} else if (e.isBoolean()) {
109+
return e.asBoolean();
110+
} else if (e.isInt()) {
111+
return e.asInt();
112+
} else if (e.isLong()) {
113+
return e.asLong();
114+
} else if (e.isBigInteger()) {
115+
return e.bigIntegerValue();
116+
} else if (e.isDouble()) {
117+
return e.doubleValue();
118+
} else if (e.isFloat()) {
119+
return e.floatValue();
120+
} else if (e.isBigDecimal()) {
121+
return e.decimalValue();
122+
} else if (e.isNull()) {
123+
return null;
124+
}
125+
}
126+
return o;
127+
}
128+
129+
@Override
130+
public boolean isArray(Object obj) {
131+
return (obj instanceof ArrayNode || obj instanceof List);
132+
}
133+
134+
@Override
135+
public Object getArrayIndex(Object obj, int idx) {
136+
return toJsonArray(obj).get(idx);
137+
}
138+
139+
@Override
140+
public void setArrayIndex(Object array, int index, Object newValue) {
141+
if (!isArray(array)) {
142+
throw new UnsupportedOperationException();
143+
} else {
144+
ArrayNode arrayNode = toJsonArray(array);
145+
if (index == arrayNode.size()) {
146+
arrayNode.add(createJsonElement(newValue));
147+
} else {
148+
arrayNode.set(index, createJsonElement(newValue));
149+
}
150+
}
151+
}
152+
153+
@Override
154+
public Object getMapValue(Object obj, String key) {
155+
ObjectNode jsonObject = toJsonObject(obj);
156+
Object o = jsonObject.get(key);
157+
if (!jsonObject.has(key)) {
158+
return UNDEFINED;
159+
} else {
160+
return o;
161+
}
162+
}
163+
164+
@Override
165+
public void setProperty(Object obj, Object key, Object value) {
166+
// jlolling: Bug: #211 avoid create cloned nodes
167+
if (isMap(obj)) {
168+
setValueInObjectNode((ObjectNode) obj, key, value);
169+
} else {
170+
ArrayNode array = (ArrayNode) obj;
171+
int index;
172+
if (key != null) {
173+
index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
174+
} else {
175+
index = array.size();
176+
}
177+
if (index == array.size()) {
178+
array.add(createJsonElement(value));
179+
} else {
180+
array.set(index, createJsonElement(value));
181+
}
182+
}
183+
}
184+
185+
public void removeProperty(Object obj, Object key) {
186+
if (isMap(obj)) {
187+
toJsonObject(obj).remove(key.toString());
188+
} else {
189+
ArrayNode array = toJsonArray(obj);
190+
int index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
191+
array.remove(index);
192+
}
193+
}
194+
195+
@Override
196+
public boolean isMap(Object obj) {
197+
return (obj instanceof ObjectNode);
198+
}
199+
200+
@Override
201+
public Collection<String> getPropertyKeys(Object obj) {
202+
return toJsonObject(obj).propertyNames();
203+
}
204+
205+
@Override
206+
public int length(Object obj) {
207+
if (isArray(obj)) {
208+
return toJsonArray(obj).size();
209+
} else if (isMap(obj)) {
210+
return toJsonObject(obj).size();
211+
} else {
212+
if (obj instanceof StringNode) {
213+
StringNode element = (StringNode) obj;
214+
return element.size();
215+
}
216+
}
217+
throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName() : "null"));
218+
}
219+
220+
@Override
221+
public Iterable<?> toIterable(Object obj) {
222+
ArrayNode arr = toJsonArray(obj);
223+
Iterator<?> iterator = arr.iterator();
224+
return new Iterable<Object>() {
225+
@Override
226+
public Iterator<Object> iterator() {
227+
return new Iterator<Object>() {
228+
@Override
229+
public boolean hasNext() {
230+
return iterator.hasNext();
231+
}
232+
233+
@Override
234+
public Object next() {
235+
return unwrap(iterator.next());
236+
}
237+
};
238+
}
239+
};
240+
}
241+
242+
private JsonNode createJsonElement(Object o) {
243+
if (o != null) {
244+
// jlolling: avoid creating a cloned node: bug #211
245+
if (o instanceof JsonNode) {
246+
return (JsonNode) o;
247+
} else {
248+
return objectMapper.valueToTree(o);
249+
}
250+
} else {
251+
return null;
252+
}
253+
}
254+
255+
private ArrayNode toJsonArray(Object o) {
256+
return (ArrayNode) o;
257+
}
258+
259+
private ObjectNode toJsonObject(Object o) {
260+
return (ObjectNode) o;
261+
}
262+
263+
private void setValueInObjectNode(ObjectNode objectNode, Object key, Object value) {
264+
// jlolling: necessary to avoid deprecated methods and to avoid creating a cloned node. Bug: #211
265+
if (value instanceof JsonNode) {
266+
objectNode.set(key.toString(), (JsonNode) value);
267+
} else if (value instanceof String) {
268+
objectNode.put(key.toString(), (String) value);
269+
} else if (value instanceof Integer) {
270+
objectNode.put(key.toString(), (Integer) value);
271+
} else if (value instanceof Long) {
272+
objectNode.put(key.toString(), (Long) value);
273+
} else if (value instanceof Short) {
274+
objectNode.put(key.toString(), (Short) value);
275+
} else if (value instanceof BigInteger) {
276+
objectNode.put(key.toString(), (BigInteger) value);
277+
} else if (value instanceof Double) {
278+
objectNode.put(key.toString(), (Double) value);
279+
} else if (value instanceof Float) {
280+
objectNode.put(key.toString(), (Float) value);
281+
} else if (value instanceof BigDecimal) {
282+
objectNode.put(key.toString(), (BigDecimal) value);
283+
} else if (value instanceof Boolean) {
284+
objectNode.put(key.toString(), (Boolean) value);
285+
} else if (value instanceof byte[]) {
286+
objectNode.put(key.toString(), (byte[]) value);
287+
} else if (value == null) {
288+
objectNode.set(key.toString(), null); // this will create a null-node
289+
} else {
290+
objectNode.set(key.toString(), createJsonElement(value));
291+
}
292+
}
293+
294+
}

0 commit comments

Comments
 (0)