Skip to content

Commit 340765f

Browse files
wnm3claude
andcommitted
Migrate from Jackson 2.x to Jackson 3 (tools.jackson) (#430)
Move the codebase from com.fasterxml.jackson 2.x to tools.jackson 3.2.0. - pom.xml: use tools.jackson.core:jackson-databind and tools.jackson.dataformat:jackson-dataformat-xml 3.2.0; remove the Jackson 2.x databind and dataformat-xml deps (woodstox-core retained); bump project version 2.6.4 -> 3.0.0. - Package/class/method renames across main and test sources: com.fasterxml.jackson.* -> tools.jackson.*, TextNode -> StringNode, JsonFactory -> tools.jackson.core.json.JsonFactory, JsonProcessingException -> JacksonException, JsonMappingException -> DatabindException, immutable mapper builders. Behavior-preserving fixes for Jackson 3 default/API changes (no test assertion or expected value was changed): - MatchFunction/ReplaceFunction: guard the pattern emptiness checks so a POJONode-wrapped RegularExpression never reaches the now-throwing asText(); logic is identical to Jackson 2. - ArrayUtils.compare: pass "" default to asText() so container operands coerce to "" as they did under Jackson 2 instead of throwing. - AgnosticTestSuite: normalize whole-number doubles with (long) d, replicating Jackson 2's silent narrowing (Jackson 3's asLong() throws on out-of-long-range values). - Utils test mapper: disable FAIL_ON_TRAILING_TOKENS to restore the Jackson 2 default so existing test inputs parse identically. - catch (IOException) -> catch (JacksonException) where Jackson 3 methods no longer throw checked IOException. - README and launch scripts (tester.sh, testerui.sh, testerui.cmd): version 2.6.4 -> 3.0.0. Verified: mvn clean test green (1223 tests, 0 failures, 93 skipped); mvn package builds JSONata4Java-3.0.0.jar with an OSGi Import-Package header referencing only tools.jackson.* [3.2,4); Tester CLI evaluation and XML round-trip smoke-tested. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e352b89 commit 340765f

108 files changed

Lines changed: 577 additions & 542 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The easiest way to use this library is to include it as a dependency in your Mav
1414
<dependency>
1515
<groupId>com.ibm.jsonata4java</groupId>
1616
<artifactId>JSONata4Java</artifactId>
17-
<version>2.6.4</version>
17+
<version>3.0.0</version>
1818
</dependency>
1919
```
2020

@@ -51,8 +51,8 @@ Note: to build and deploy the jars to Maven Central you need to use a command li
5151
`mvn clean install deploy -Prelease`
5252

5353
Once you have run the launcher, you can find the jar files in the /target directory. There are two&colon;
54-
* **JSONata4Java-2.6.4-jar-with-dependencies.jar** (thinks includes dependent jar files)
55-
* **JSONata4Java-2.6.4.jar** (only the JSONata4Java code)
54+
* **JSONata4Java-3.0.0-jar-with-dependencies.jar** (thinks includes dependent jar files)
55+
* **JSONata4Java-3.0.0.jar** (only the JSONata4Java code)
5656

5757
The com.api.jsonata4java.Tester program enables you to enter an expression and run it
5858
against the same JSON as is used at the https://try.jsonata.org site. You can also

docs/plans/Jackson-3-Migration-Plan.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,39 @@ find src/main/java src/test/java -name '*.java' -print0 | xargs -0 perl -pi -e '
9292
'
9393
```
9494

95+
- [ ] **Step 1b: Apply Jackson 3 API class/method renames (verified against the 3.2.0 jar)**
96+
97+
Jackson 3 renamed several `JsonNode` types and methods, not just packages. These were confirmed by inspecting `jackson-databind-3.2.0.jar` / `jackson-core-3.2.0.jar`:
98+
99+
- `TextNode``StringNode` (the class `tools.jackson.databind.node.TextNode` does not exist; `StringNode` replaces it). Affects imports, `new TextNode(...)`, `TextNode.valueOf(...)`, and `(TextNode)` casts. ~106 refs.
100+
- `tools.jackson.core.JsonProcessingException``tools.jackson.core.JacksonException` (JsonProcessingException is gone from core; `JacksonException` is the unchecked base). Affects imports and `catch` clauses. ~28 refs.
101+
- `.fieldNames()` (returned `Iterator<String>` in 2.x) → `.propertyNames()` (returns `Collection<String>` in 3.x). To preserve `Iterator`-based loops, replace `X.fieldNames()` with `X.propertyNames().iterator()`. ~21 calls.
102+
- `.elements()` (returned `Iterator<JsonNode>` in 2.x) → `.values()` (returns `Collection<JsonNode>` in 3.x). Replace `X.elements()` with `X.values().iterator()`. ~5 calls.
103+
- `.fields()``.properties()` if present. (This codebase has **0** `.fields()` calls — nothing to do, but do not introduce any.)
104+
- NOTE: `textValue()` and `asText()` **still exist** on `JsonNode` in 3.x — do NOT rewrite those.
105+
106+
Apply the safe global renames with word-boundary care, then let the compiler catch the rest:
107+
108+
```bash
109+
find src/main/java src/test/java -name '*.java' -print0 | xargs -0 perl -pi -e '
110+
s/\bTextNode\b/StringNode/g;
111+
s/\bJsonProcessingException\b/JacksonException/g;
112+
s/\.fieldNames\(\)/.propertyNames().iterator()/g;
113+
s/\.elements\(\)/.values().iterator()/g;
114+
'
115+
```
116+
117+
Note: `TextNode``StringNode` also renames the import line `import tools.jackson.databind.node.TextNode;` to `import tools.jackson.databind.node.StringNode;`, which is correct. Watch for identifiers that merely contain "TextNode" as a substring — the `\b` boundaries prevent partial-word hits, but verify at compile time.
118+
95119
- [ ] **Step 2: Confirm no `com.fasterxml.jackson` core/databind/xml references remain**
96120

97121
Run: `grep -rn "com.fasterxml.jackson.core\|com.fasterxml.jackson.databind\|com.fasterxml.jackson.dataformat" src/ || echo CLEAN`
98122
Expected: `CLEAN` (annotations, if any ever appear, are intentionally left; none exist today).
99123

100-
- [ ] **Step 3: Compile to surface the hotspots**
124+
- [ ] **Step 3: Compile to surface the remaining hotspots**
101125

102-
Run: `mvn -q -DskipTests clean compile 2>&1 | tee /tmp/j3-compile.log | tail -40`
103-
Expected: FAILS — errors concentrated in `Tester.java` (`getFactory().configure`, `JsonWriteFeature.mappedFeature()`) and `TesterUI.java` (`xmlMapper.enable`/`.configure`, `ToXmlGenerator.Feature`). Note each reported file:line.
126+
Run: `mvn -q -DskipTests clean compile 2>&1 | tee /tmp/j3-compile.log | grep -E "ERROR|\.java:" | head -50`
127+
Expected: FAILS — after Step 1b the remaining errors are concentrated in `Tester.java` (`getFactory().configure`, `JsonWriteFeature.mappedFeature()`) and `TesterUI.java` (`xmlMapper.enable`/`.configure`, `ToXmlGenerator.Feature`), plus any stragglers from the method renames. Note each reported file:line and fix iteratively.
104128

105129
- [ ] **Step 4: Fix `Tester.java` mapper construction**
106130

pom.xml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<modelVersion>4.0.0</modelVersion>
2828
<groupId>com.ibm.jsonata4java</groupId>
2929
<artifactId>JSONata4Java</artifactId>
30-
<version>2.6.4</version>
30+
<version>3.0.0</version>
3131
<name>JSONata4Java</name>
3232
<description>Port of jsonata.js to Java to enable rules for JSON content</description>
3333
<url>https://github.com/IBM/JSONata4Java</url>
@@ -121,22 +121,19 @@
121121
<version>3.2.0</version>
122122
<scope>compile</scope>
123123
</dependency>
124+
<!-- Source: https://mvnrepository.com/artifact/tools.jackson.dataformat/jackson-dataformat-xml -->
124125
<dependency>
125-
<groupId>com.fasterxml.jackson.core</groupId>
126-
<artifactId>jackson-databind</artifactId>
127-
<version>2.22.0</version>
126+
<groupId>tools.jackson.dataformat</groupId>
127+
<artifactId>jackson-dataformat-xml</artifactId>
128+
<version>3.2.0</version>
129+
<scope>compile</scope>
128130
</dependency>
129131
<!-- https://mvnrepository.com/artifact/com.fasterxml.woodstox/woodstox-core -->
130132
<dependency>
131133
<groupId>com.fasterxml.woodstox</groupId>
132134
<artifactId>woodstox-core</artifactId>
133135
<version>7.2.1</version>
134136
</dependency>
135-
<dependency>
136-
<groupId>com.fasterxml.jackson.dataformat</groupId>
137-
<artifactId>jackson-dataformat-xml</artifactId>
138-
<version>2.22.0</version>
139-
</dependency>
140137
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-text -->
141138
<dependency>
142139
<groupId>org.apache.commons</groupId>

src/main/java/com/api/jsonata4java/Binding.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import com.api.jsonata4java.expressions.generated.MappingExpressionParser.NullContext;
2323
import com.api.jsonata4java.expressions.generated.MappingExpressionParser.NumberContext;
2424
import com.api.jsonata4java.expressions.generated.MappingExpressionParser.StringContext;
25-
import com.fasterxml.jackson.databind.JsonNode;
26-
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import tools.jackson.databind.JsonNode;
26+
import tools.jackson.databind.ObjectMapper;
2727

2828
/**
2929
* Class mapping a variable name to a variable or function declaration

src/main/java/com/api/jsonata4java/Expression.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
import com.api.jsonata4java.expressions.functions.DeclaredFunction;
2121
import com.api.jsonata4java.expressions.generated.MappingExpressionParser.ExprContext;
2222
import com.api.jsonata4java.expressions.regex.RegexEngine;
23-
import com.fasterxml.jackson.databind.JsonNode;
24-
import com.fasterxml.jackson.databind.ObjectMapper;
25-
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
23+
import tools.jackson.databind.JsonNode;
24+
import tools.jackson.databind.ObjectMapper;
25+
import tools.jackson.databind.node.JsonNodeFactory;
2626

2727
/**
2828
* Class to provide embedding and extending JSONata features
@@ -42,7 +42,7 @@ public class Expression implements Serializable {
4242
public static List<Binding> createBindings(JsonNode bindingObj) throws ParseException {
4343
ObjectMapper objectMapper = new ObjectMapper();
4444
List<Binding> bindings = new ArrayList<Binding>();
45-
for (Iterator<String> it = bindingObj.fieldNames(); it.hasNext();) {
45+
for (Iterator<String> it = bindingObj.propertyNames().iterator(); it.hasNext();) {
4646
String key = it.next();
4747
JsonNode testObj = bindingObj.get(key);
4848
String expression = "";

src/main/java/com/api/jsonata4java/JSONataUtils.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@
5353
//import java.rmi.dgc.VMID;
5454
import java.util.UUID;
5555
import javax.management.modelmbean.InvalidTargetObjectTypeException;
56-
import com.fasterxml.jackson.databind.JsonNode;
57-
import com.fasterxml.jackson.databind.ObjectMapper;
58-
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
59-
import com.fasterxml.jackson.databind.node.NullNode;
56+
import tools.jackson.databind.JsonNode;
57+
import tools.jackson.databind.ObjectMapper;
58+
import tools.jackson.databind.node.JsonNodeFactory;
59+
import tools.jackson.databind.node.NullNode;
6060

6161
public class JSONataUtils implements Serializable {
6262

src/main/java/com/api/jsonata4java/Sequence.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424

2525
import java.io.Serializable;
2626
import java.util.List;
27-
import com.fasterxml.jackson.databind.JsonNode;
28-
import com.fasterxml.jackson.databind.node.ArrayNode;
29-
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
30-
import com.fasterxml.jackson.databind.node.ObjectNode;
27+
import tools.jackson.databind.JsonNode;
28+
import tools.jackson.databind.node.ArrayNode;
29+
import tools.jackson.databind.node.JsonNodeFactory;
30+
import tools.jackson.databind.node.ObjectNode;
3131

3232
public class Sequence implements Serializable {
3333

src/main/java/com/api/jsonata4java/Signature.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
import com.api.jsonata4java.expressions.functions.DeclaredFunction;
3434
import com.api.jsonata4java.expressions.generated.MappingExpressionParser.ExprContext;
3535
import com.api.jsonata4java.expressions.generated.MappingExpressionParser.ExprListContext;
36-
import com.fasterxml.jackson.databind.JsonNode;
37-
import com.fasterxml.jackson.databind.node.ArrayNode;
38-
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
39-
import com.fasterxml.jackson.databind.node.ObjectNode;
36+
import tools.jackson.databind.JsonNode;
37+
import tools.jackson.databind.node.ArrayNode;
38+
import tools.jackson.databind.node.JsonNodeFactory;
39+
import tools.jackson.databind.node.ObjectNode;
4040

4141
/**
4242
* Manages signature related functions

src/main/java/com/api/jsonata4java/Test.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import com.api.jsonata4java.expressions.EvaluateRuntimeException;
77
import com.api.jsonata4java.expressions.Expressions;
88
import com.api.jsonata4java.expressions.ParseException;
9-
import com.fasterxml.jackson.core.JsonProcessingException;
10-
import com.fasterxml.jackson.databind.JsonNode;
11-
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import tools.jackson.core.JacksonException;
10+
import tools.jackson.databind.JsonNode;
11+
import tools.jackson.databind.ObjectMapper;
1212

1313
public class Test implements Serializable {
1414

@@ -22,7 +22,7 @@ public static void main(String[] args) {
2222
String expression = "$sum(c)";
2323
try {
2424
jsonObj = mapper.readTree(json);
25-
} catch (IOException e1) {
25+
} catch (JacksonException e1) {
2626
e1.printStackTrace();
2727
}
2828

@@ -34,7 +34,7 @@ public static void main(String[] args) {
3434
System.err.println(e.getLocalizedMessage());
3535
} catch (EvaluateRuntimeException ere) {
3636
System.err.println(ere.getLocalizedMessage());
37-
} catch (JsonProcessingException e) {
37+
} catch (JacksonException e) {
3838
e.printStackTrace();
3939
} catch (IOException ioe) {
4040
System.err.println(ioe.getLocalizedMessage());
@@ -47,7 +47,7 @@ public static void main(String[] args) {
4747
} else {
4848
System.out.println("" + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result));
4949
}
50-
} catch (EvaluateException | JsonProcessingException e) {
50+
} catch (EvaluateException | JacksonException e) {
5151
System.err.println(e.getLocalizedMessage());
5252
}
5353
}

src/main/java/com/api/jsonata4java/Tester.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929
import com.api.jsonata4java.expressions.EvaluateRuntimeException;
3030
import com.api.jsonata4java.expressions.Expressions;
3131
import com.api.jsonata4java.expressions.ParseException;
32-
import com.fasterxml.jackson.core.JsonProcessingException;
33-
import com.fasterxml.jackson.core.json.JsonWriteFeature;
34-
import com.fasterxml.jackson.databind.JsonNode;
35-
import com.fasterxml.jackson.databind.ObjectMapper;
36-
import com.fasterxml.jackson.databind.node.TextNode;
32+
import tools.jackson.core.JacksonException;
33+
import tools.jackson.core.json.JsonWriteFeature;
34+
import tools.jackson.databind.JsonNode;
35+
import tools.jackson.databind.json.JsonMapper;
36+
import tools.jackson.databind.node.StringNode;
3737

3838
/**
3939
* Expression evaluation test utility
@@ -120,12 +120,13 @@ public class Tester implements Serializable {
120120
* @param args a fully qualified path and filename for test JSON could be provided
121121
*/
122122
public static void main(String[] args) {
123-
ObjectMapper mapper = new ObjectMapper();
124-
mapper.getFactory().configure(JsonWriteFeature.ESCAPE_NON_ASCII.mappedFeature(), true);
123+
JsonMapper mapper = JsonMapper.builder()
124+
.enable(JsonWriteFeature.ESCAPE_NON_ASCII)
125+
.build();
125126
JsonNode jsonObj = null;
126127
try {
127128
jsonObj = mapper.readTree(json);
128-
} catch (IOException e1) {
129+
} catch (JacksonException e1) {
129130
e1.printStackTrace();
130131
}
131132
if (args.length > 0) {
@@ -135,21 +136,19 @@ public static void main(String[] args) {
135136
System.out.println("Attempting to load JSON from file: " + args[0]);
136137
try {
137138
jsonObj = mapper.readTree(file);
138-
} catch (JsonProcessingException e) {
139-
System.err.println(e.getLocalizedMessage());
140-
} catch (IOException e) {
139+
} catch (JacksonException e) {
141140
System.err.println(e.getLocalizedMessage());
142141
}
143142
} else {
144-
jsonObj = new TextNode(args[0]);
143+
jsonObj = new StringNode(args[0]);
145144
}
146145
} else {
147146
jsonObj = null;
148147
}
149148
}
150149
try {
151150
System.out.println("Using json:\n" + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObj));
152-
} catch (JsonProcessingException e1) {
151+
} catch (JacksonException e1) {
153152
e1.printStackTrace();
154153
}
155154
while (true) {
@@ -187,7 +186,7 @@ public static void main(String[] args) {
187186
} else {
188187
System.out.println("" + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result));
189188
}
190-
} catch (EvaluateException | JsonProcessingException e) {
189+
} catch (EvaluateException | JacksonException e) {
191190
System.err.println(e.getLocalizedMessage());
192191
}
193192
}

0 commit comments

Comments
 (0)