Skip to content

Commit ccbff25

Browse files
feat: migrate to Jackson 3
Migrates the SDK from Jackson 2.17.3 to Jackson 3.0.0. Closes #370. - build.gradle: use the tools.jackson BOM (jackson-databind 3.0.0); jackson-annotations stays on com.fasterxml.jackson per Jackson 3 - imports moved from com.fasterxml.jackson.{core,databind} to tools.jackson.* (annotations package unchanged) - JacksonJsonTransformer: ObjectMapper is immutable in 3.x, rebuilt via JsonMapper.builder() (configure, changeDefaultPropertyInclusion(NON_NULL), defaultDateFormat, addModule, changeDefaultVisibility); derived mappers via rebuild() - custom (de)serializers ported: JsonSerializer/JsonDeserializer -> ValueSerializer/ValueDeserializer, SerializerProvider -> SerializationContext, ContextualDeserializer removed (createContextual now declared on ValueDeserializer), p.getCodec().readTree(p) -> ctxt.readTree(p), getCurrentToken() -> currentToken(), node.fieldNames() -> propertyNames(), exceptions are unchecked (dropped throws IOException) - tests: rewrote the deserializer unit tests that relied on removed Jackson 2 internals (JsonParser.setCodec, ObjectMapper.getFactory/getDeserializationContext) to drive deserialization through a configured mapper BREAKING CHANGE: now requires Jackson 3 (tools.jackson coordinates). Jackson 3's StdDateFormat serializes UTC dates as "...Z" (canonical ISO-8601) instead of "...+00:00"; the affected request fixture was updated accordingly. All 579 tests pass.
1 parent 8c13fcc commit ccbff25

24 files changed

Lines changed: 175 additions & 164 deletions

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ dependencies {
2727
annotationProcessor 'org.projectlombok:lombok:1.18.32'
2828

2929
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
30-
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.3'
30+
implementation platform('tools.jackson:jackson-bom:3.0.0')
31+
implementation 'tools.jackson.core:jackson-databind'
3132

3233
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0','org.mockito:mockito-core:4.11.0'
3334
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'

src/main/java/com/crowdin/client/core/http/impl/json/CrowdinApiExceptionDeserializer.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import com.crowdin.client.core.http.exceptions.HttpBadRequestException;
55
import com.crowdin.client.core.http.exceptions.HttpBatchBadRequestException;
66
import com.crowdin.client.core.http.exceptions.HttpException;
7-
import com.fasterxml.jackson.core.JsonParser;
8-
import com.fasterxml.jackson.core.TreeNode;
9-
import com.fasterxml.jackson.databind.DeserializationContext;
10-
import com.fasterxml.jackson.databind.JsonDeserializer;
11-
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import tools.jackson.core.JsonParser;
8+
import tools.jackson.databind.JsonNode;
9+
import tools.jackson.databind.DeserializationContext;
10+
import tools.jackson.databind.ValueDeserializer;
11+
import tools.jackson.databind.ObjectMapper;
1212

1313
import java.io.IOException;
1414

15-
public class CrowdinApiExceptionDeserializer extends JsonDeserializer<CrowdinApiException> {
15+
public class CrowdinApiExceptionDeserializer extends ValueDeserializer<CrowdinApiException> {
1616

1717
private final ObjectMapper objectMapper;
1818

@@ -21,12 +21,12 @@ public CrowdinApiExceptionDeserializer(ObjectMapper objectMapper) {
2121
}
2222

2323
@Override
24-
public CrowdinApiException deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
25-
TreeNode treeNode = p.getCodec().readTree(p);
26-
TreeNode errors = treeNode.get("errors");
24+
public CrowdinApiException deserialize(JsonParser p, DeserializationContext ctxt) {
25+
JsonNode treeNode = ctxt.readTree(p);
26+
JsonNode errors = treeNode.get("errors");
2727

2828
if (errors != null) {
29-
TreeNode firstElement = errors.get(0);
29+
JsonNode firstElement = errors.get(0);
3030

3131
if (firstElement != null && firstElement.get("index") != null) {
3232
return this.objectMapper.treeToValue(treeNode, HttpBatchBadRequestException.class);

src/main/java/com/crowdin/client/core/http/impl/json/DateDeserializer.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package com.crowdin.client.core.http.impl.json;
22

3-
import com.fasterxml.jackson.core.JsonParser;
4-
import com.fasterxml.jackson.databind.DeserializationContext;
5-
import com.fasterxml.jackson.databind.JsonDeserializer;
3+
import tools.jackson.core.JsonParser;
4+
import tools.jackson.databind.DeserializationContext;
5+
import tools.jackson.databind.ValueDeserializer;
66
import lombok.SneakyThrows;
77

88
import java.io.IOException;
99
import java.text.SimpleDateFormat;
1010
import java.util.Date;
1111

12-
public class DateDeserializer extends JsonDeserializer<Date> {
12+
public class DateDeserializer extends ValueDeserializer<Date> {
1313

1414
@Override
1515
@SneakyThrows
16-
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
16+
public Date deserialize(JsonParser p, DeserializationContext ctxt) {
1717
String date = p.getText();
1818
if (date == null || date.isEmpty()) {
1919
return null;

src/main/java/com/crowdin/client/core/http/impl/json/EmptyArrayToNullDeserializer.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package com.crowdin.client.core.http.impl.json;
22

3-
import com.fasterxml.jackson.core.JsonParser;
4-
import com.fasterxml.jackson.core.JsonToken;
5-
import com.fasterxml.jackson.databind.*;
6-
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
7-
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
3+
import tools.jackson.core.JsonParser;
4+
import tools.jackson.core.JsonToken;
5+
import tools.jackson.databind.*;
6+
import tools.jackson.databind.deser.std.StdDeserializer;
87

98
import java.io.IOException;
109
import java.util.Collection;
1110

12-
public class EmptyArrayToNullDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
11+
public class EmptyArrayToNullDeserializer extends StdDeserializer<Object> {
1312
private JavaType type;
1413

1514
public EmptyArrayToNullDeserializer() {
@@ -22,14 +21,14 @@ public EmptyArrayToNullDeserializer(JavaType type) {
2221
}
2322

2423
@Override
25-
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
26-
if (p.getCurrentToken() == JsonToken.VALUE_NULL) {
24+
public Object deserialize(JsonParser p, DeserializationContext ctxt) {
25+
if (p.currentToken() == JsonToken.VALUE_NULL) {
2726
return null;
2827
}
2928

3029
Class<?> clazz = this.type != null ? this.type.getRawClass() : Object.class;
3130

32-
if (p.getCurrentToken() == JsonToken.START_ARRAY) {
31+
if (p.currentToken() == JsonToken.START_ARRAY) {
3332
if (!isCollectionType(clazz)) {
3433
p.nextToken();
3534
return null;
@@ -46,7 +45,7 @@ private static boolean isCollectionType(Class<?> type) {
4645
}
4746

4847
@Override
49-
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
48+
public ValueDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
5049
return new EmptyArrayToNullDeserializer(property.getType());
5150
}
5251
}

src/main/java/com/crowdin/client/core/http/impl/json/EnumDeserializer.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package com.crowdin.client.core.http.impl.json;
22

3-
import com.fasterxml.jackson.core.JsonParser;
4-
import com.fasterxml.jackson.core.JsonProcessingException;
5-
import com.fasterxml.jackson.databind.BeanProperty;
6-
import com.fasterxml.jackson.databind.DeserializationContext;
7-
import com.fasterxml.jackson.databind.JavaType;
8-
import com.fasterxml.jackson.databind.JsonDeserializer;
9-
import com.fasterxml.jackson.databind.JsonMappingException;
10-
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
3+
import tools.jackson.core.JsonParser;
4+
import tools.jackson.core.JacksonException;
5+
import tools.jackson.databind.BeanProperty;
6+
import tools.jackson.databind.DeserializationContext;
7+
import tools.jackson.databind.JavaType;
8+
import tools.jackson.databind.ValueDeserializer;
9+
import tools.jackson.databind.DatabindException;
1110
import com.crowdin.client.core.model.EnumConverter;
1211
import lombok.SneakyThrows;
1312

1413
import java.io.IOException;
1514

16-
public class EnumDeserializer extends JsonDeserializer<Enum> implements ContextualDeserializer {
15+
public class EnumDeserializer extends ValueDeserializer<Enum> {
1716

1817
private JavaType type;
1918

@@ -25,7 +24,7 @@ public EnumDeserializer(JavaType type) {
2524
}
2625

2726
@Override
28-
public JsonDeserializer<?> createContextual(DeserializationContext deserializationContext, BeanProperty beanProperty) throws JsonMappingException {
27+
public ValueDeserializer<?> createContextual(DeserializationContext deserializationContext, BeanProperty beanProperty) throws DatabindException {
2928
//beanProperty is null when the type to deserialize is the top-level type or a generic type, not a type of a bean property
3029
JavaType type = deserializationContext.getContextualType() != null
3130
? deserializationContext.getContextualType()
@@ -34,7 +33,7 @@ public JsonDeserializer<?> createContextual(DeserializationContext deserializati
3433
}
3534

3635
@Override
37-
public Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
36+
public Enum deserialize(JsonParser p, DeserializationContext ctxt) {
3837
String text = p.getText();
3938
return this.deserialize((Class<? extends Enum>) this.type.getRawClass(), text);
4039
}

src/main/java/com/crowdin/client/core/http/impl/json/EnumSerializer.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package com.crowdin.client.core.http.impl.json;
22

3-
import com.fasterxml.jackson.core.JsonGenerator;
4-
import com.fasterxml.jackson.databind.JsonSerializer;
5-
import com.fasterxml.jackson.databind.SerializerProvider;
3+
import tools.jackson.core.JsonGenerator;
4+
import tools.jackson.databind.ValueSerializer;
5+
import tools.jackson.databind.SerializationContext;
66
import com.crowdin.client.core.model.EnumConverter;
77
import lombok.SneakyThrows;
88

99
import java.io.IOException;
1010

11-
public class EnumSerializer extends JsonSerializer<Enum> {
11+
public class EnumSerializer extends ValueSerializer<Enum> {
1212
@Override
13-
public void serialize(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
13+
public void serialize(Enum value, JsonGenerator gen, SerializationContext serializers) {
1414
Object val;
1515
if (value instanceof EnumConverter) {
1616
val = this.serialize(value, EnumConverter.class.cast(value));

src/main/java/com/crowdin/client/core/http/impl/json/FileExportOptionsDeserializer.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@
44
import com.crowdin.client.sourcefiles.model.GeneralFileExportOptions;
55
import com.crowdin.client.sourcefiles.model.JavaScriptFileExportOptions;
66
import com.crowdin.client.sourcefiles.model.PropertyFileExportOptions;
7-
import com.fasterxml.jackson.core.JsonParser;
8-
import com.fasterxml.jackson.core.JsonProcessingException;
9-
import com.fasterxml.jackson.core.TreeNode;
10-
import com.fasterxml.jackson.databind.DeserializationContext;
11-
import com.fasterxml.jackson.databind.JsonDeserializer;
12-
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import tools.jackson.core.JsonParser;
8+
import tools.jackson.core.JacksonException;
9+
import tools.jackson.databind.JsonNode;
10+
import tools.jackson.databind.DeserializationContext;
11+
import tools.jackson.databind.ValueDeserializer;
12+
import tools.jackson.databind.ObjectMapper;
1313

1414
import java.io.IOException;
1515
import java.util.List;
1616
import java.util.stream.Collectors;
1717
import java.util.stream.StreamSupport;
1818

19-
public class FileExportOptionsDeserializer extends JsonDeserializer<ExportOptions> {
19+
public class FileExportOptionsDeserializer extends ValueDeserializer<ExportOptions> {
2020

2121
private final ObjectMapper objectMapper;
2222

@@ -25,9 +25,9 @@ public FileExportOptionsDeserializer(ObjectMapper objectMapper) {
2525
}
2626

2727
@Override
28-
public ExportOptions deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
29-
TreeNode treeNode = p.getCodec().readTree(p);
30-
Iterable<String> iterable = treeNode::fieldNames;
28+
public ExportOptions deserialize(JsonParser p, DeserializationContext ctxt) {
29+
JsonNode treeNode = ctxt.readTree(p);
30+
Iterable<String> iterable = treeNode.propertyNames();
3131
List<String> fields = StreamSupport
3232
.stream(iterable.spliterator(), false)
3333
.collect(Collectors.toList());

src/main/java/com/crowdin/client/core/http/impl/json/FileFormatSettingsDeserializer.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package com.crowdin.client.core.http.impl.json;
22

33
import com.crowdin.client.projectsgroups.model.*;
4-
import com.fasterxml.jackson.core.JsonParser;
5-
import com.fasterxml.jackson.databind.DeserializationContext;
6-
import com.fasterxml.jackson.databind.JsonDeserializer;
7-
import com.fasterxml.jackson.databind.JsonNode;
8-
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import tools.jackson.core.JsonParser;
5+
import tools.jackson.databind.DeserializationContext;
6+
import tools.jackson.databind.ValueDeserializer;
7+
import tools.jackson.databind.JsonNode;
8+
import tools.jackson.databind.ObjectMapper;
99

1010
import java.io.IOException;
1111

12-
public class FileFormatSettingsDeserializer extends JsonDeserializer<FileFormatSettingsResource> {
12+
public class FileFormatSettingsDeserializer extends ValueDeserializer<FileFormatSettingsResource> {
1313

1414
private final ObjectMapper objectMapper;
1515

@@ -18,8 +18,8 @@ public FileFormatSettingsDeserializer(ObjectMapper objectMapper) {
1818
}
1919

2020
@Override
21-
public FileFormatSettingsResource deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
22-
JsonNode parentNode = parser.getCodec().readTree(parser);
21+
public FileFormatSettingsResource deserialize(JsonParser parser, DeserializationContext ctxt) {
22+
JsonNode parentNode = ctxt.readTree(parser);
2323

2424
FileFormatSettingsResource resource = this.objectMapper.readValue(parentNode.toString(), FileFormatSettingsResource.class);
2525

src/main/java/com/crowdin/client/core/http/impl/json/FileImportOptionsDeserializer.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
import com.crowdin.client.sourcefiles.model.OtherFileImportOptions;
66
import com.crowdin.client.sourcefiles.model.SpreadsheetFileImportOptions;
77
import com.crowdin.client.sourcefiles.model.XmlFileImportOptions;
8-
import com.fasterxml.jackson.core.JsonParser;
9-
import com.fasterxml.jackson.core.JsonProcessingException;
10-
import com.fasterxml.jackson.core.TreeNode;
11-
import com.fasterxml.jackson.databind.DeserializationContext;
12-
import com.fasterxml.jackson.databind.JsonDeserializer;
13-
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import tools.jackson.core.JsonParser;
9+
import tools.jackson.core.JacksonException;
10+
import tools.jackson.databind.JsonNode;
11+
import tools.jackson.databind.DeserializationContext;
12+
import tools.jackson.databind.ValueDeserializer;
13+
import tools.jackson.databind.ObjectMapper;
1414

1515
import java.io.IOException;
1616
import java.util.List;
1717
import java.util.stream.Collectors;
1818
import java.util.stream.StreamSupport;
1919

20-
public class FileImportOptionsDeserializer extends JsonDeserializer<ImportOptions> {
20+
public class FileImportOptionsDeserializer extends ValueDeserializer<ImportOptions> {
2121

2222
private final ObjectMapper objectMapper;
2323

@@ -26,9 +26,9 @@ public FileImportOptionsDeserializer(ObjectMapper objectMapper) {
2626
}
2727

2828
@Override
29-
public ImportOptions deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
30-
TreeNode treeNode = p.getCodec().readTree(p);
31-
Iterable<String> iterable = treeNode::fieldNames;
29+
public ImportOptions deserialize(JsonParser p, DeserializationContext ctxt) {
30+
JsonNode treeNode = ctxt.readTree(p);
31+
Iterable<String> iterable = treeNode.propertyNames();
3232
List<String> fields = StreamSupport
3333
.stream(iterable.spliterator(), false)
3434
.collect(Collectors.toList());

src/main/java/com/crowdin/client/core/http/impl/json/FileInfoDeserializer.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
import com.crowdin.client.sourcefiles.model.File;
44
import com.crowdin.client.sourcefiles.model.FileInfo;
5-
import com.fasterxml.jackson.core.JsonParser;
6-
import com.fasterxml.jackson.core.JsonProcessingException;
7-
import com.fasterxml.jackson.core.TreeNode;
8-
import com.fasterxml.jackson.databind.DeserializationContext;
9-
import com.fasterxml.jackson.databind.JsonDeserializer;
10-
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import tools.jackson.core.JsonParser;
6+
import tools.jackson.core.JacksonException;
7+
import tools.jackson.databind.JsonNode;
8+
import tools.jackson.databind.DeserializationContext;
9+
import tools.jackson.databind.ValueDeserializer;
10+
import tools.jackson.databind.ObjectMapper;
1111

1212
import java.io.IOException;
1313
import java.util.List;
1414
import java.util.stream.Collectors;
1515
import java.util.stream.StreamSupport;
1616

17-
public class FileInfoDeserializer extends JsonDeserializer<FileInfo> {
17+
public class FileInfoDeserializer extends ValueDeserializer<FileInfo> {
1818

1919
private final ObjectMapper objectMapper;
2020

@@ -23,9 +23,9 @@ public FileInfoDeserializer(ObjectMapper objectMapper) {
2323
}
2424

2525
@Override
26-
public FileInfo deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
27-
TreeNode treeNode = p.getCodec().readTree(p);
28-
Iterable<String> iterable = treeNode::fieldNames;
26+
public FileInfo deserialize(JsonParser p, DeserializationContext ctxt) {
27+
JsonNode treeNode = ctxt.readTree(p);
28+
Iterable<String> iterable = treeNode.propertyNames();
2929
List<String> fields = StreamSupport
3030
.stream(iterable.spliterator(), false)
3131
.collect(Collectors.toList());

0 commit comments

Comments
 (0)