I was previously using jackson-databind 2.8.11.3 + Java 8 to deserialise a class ApiRequest, which was used as a request body in an API call. It has worked fine all the while and the JSON string is able to be generated.
In the Object params, either a List of Strings or boolean can be added.
Example JSON:
{
"fields":[
{
"key": "colors",
"values": ["blue","red"]
},
{
"key": "metal",
"values": true
}
]
}
When I upgraded to use jackson-databind 2.13.2.2 + Java 11, and running the unit test for the generation of the JSON string, I ran to an issue where there was a bad JSON definition
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot
construct instance of ‘com…’ (no Creators, like default constructor,
exist): cannot deserialize from Object value (no delegate- or
property-based Creator) at [Source: (File) .. (through reference
chain: com…ApiRequest["fields"])at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)
Below is a snippet of the class, which I have extracted and renamed:
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiRequest {
private List<Fields> fields;
@Data
@Builder
public static class Fields {
private String key;
private Object values;
}
}
I have attempted to use JsonDeserializer to resolve the issue after reading up some other issues that others have posted online, but I was unsuccessful in resolving this.
Any suggestions/inputs would be greatly appreciated..
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiRequest {
private List<Fields> fields;
@Data
@Builder
@JsonDeserialize(using = FieldDeserializer.class) <-- added this
public static class Fields {
private String key;
private Object values;
}
}
public class FieldDeserializer extends JsonDeserializer<ApiRequest.Fields> {
@Override
public ApiRequest.Fields deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String key = node.get("key");
JsonNode valuesNode = node.get("values");
Object values = null;
if (valuesNode.isArray()) {
List<String> listValues = new ArrayList<>();
valuesNode.forEach(elementNode -> listValues.add(elementNode.asText()));
values = listValues;
} else if (valuesNode.isBoolean()) {
values = valuesNode.asBoolean();
}
return new ApiRequest.Fields.builder().key(key).values(values).build();
}
}
2
Answers
Do set @Jacksonized along with @Builder
Refer to Oleg’s answer if you are fine with experimental feature.
@Jacksonized
is perfect match for this scenario.Another way to solve this issue using stable API is by adding
@NoArgsConstructor
and@AllArgsConstructor
toApiRequest
andField
.@Jacksonized
does not.This could also be rectified by adding
access = AccessLevel.PRIVATE
to the annotation (suppose jackson has access to your Module).