Jackson 2.18.3 fails to deserialize classes with @Builder (Lombok) and ImmutableList (Guava) fields. This worked in Jackson 2.18.1 but breaks after upgrading to 2.18.3 (via Spring Boot 3.4.0 → 3.4.5).
Error
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of com.google.common.collect.ImmutableList
(no Creators, like default constructor, exist)
Code that fails
@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PRIVATE)
class MimirAssertsRelabelRules {
private ImmutableList<MimirRelabelRuleGroup> asserts;
}
ObjectMapper mapper = new ObjectMapper()
.registerModule(new GuavaModule());
// This throws InvalidDefinitionException
mapper.readValue(json, MimirAssertsRelabelRules.class);
Workaround that works
Add @JsonCreator to force constructor-based deserialization:
@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PRIVATE)
class MimirAssertsRelabelRules {
private ImmutableList<MimirRelabelRuleGroup> asserts;
@JsonCreator
public static MimirAssertsRelabelRules fromJson(@JsonProperty("asserts") List<MimirRelabelRuleGroup> asserts) {
return new MimirAssertsRelabelRules(
asserts != null ? ImmutableList.copyOf(asserts) : ImmutableList.of()
);
}
}
This works because it bypasses the builder pattern and uses constructor-based deserialization.
Question
Can anyone help explain why this changed between 2.18.1 and 2.18.3?
Comment From: pjfanning
The 2.18 release notes are at https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.18
Maybe, you could try 2.18.5.
Comment From: JooHyukKim
Guava version change reports also has no diff between 2.18.1 and 2.18.3.
So we can basically narrow down to databind module only, BUT it's reasonable try with Jackson-ONLY reproduction @krritik if you could provide one?
Comment From: cowtowncoder
Wrong repo, as per README should not file issues here. Will transfer to proper repo.
Comment From: cowtowncoder
Hmmmh. So, need to de-Lombok. But unfortunately there's the Guava part so may need to actually move to
https://github.com/FasterXML/jackson-datatypes-collections/
Aside from that would second suggestion to try 2.18.5 and if that won't work, 2.19.3 or 2.20.1.
Finally, aside from seeing de-Lombokified sources, actual exception would be useful.
Comment From: krritik
Thanks @cowtowncoder for moving the issue to the correct repo.
I tried 2.18.5, 2.19.3 and 2.20.1 None worked for now. I will update it with the exact error.
@JooHyukKim , let me try if I can put a more simpler Jackson-ONLY reproduction
Comment From: krritik
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.google.common.collect.ImmutableList` (no Creators, like default constructor, exist): no default no-arguments constructor found
at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 2, column: 5] (through reference chain: ai.asserts.prometheus.definition.rules.MimirAssertsRelabelRules["asserts"])
at app//com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at app//com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1888)
at app//com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414)
at app//com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1375)
at app//com.fasterxml.jackson.databind.deser.ValueInstantiator.createUsingDefault(ValueInstantiator.java:248)
at app//com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createUsingDefault(StdValueInstantiator.java:275)
at app//com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createDefaultInstance(CollectionDeserializer.java:264)
at app//com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
at app//com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
at app//com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at app//com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:399)
at app//com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at app//com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
at app//com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2131)
at app//com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1501)
at app//org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:397)
... 45 more
This is the error trace for now. If it is of any help.
Comment From: JooHyukKim
If adding Creator works, small posibility its Guava issue.
Let's wait for de Lomboked structure and continue investigating further
Comment From: cowtowncoder
Problem could be due to
at app//org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:397)
that is, depends on details of how Spring calls Jackson.
But yes, we need a stand-alone reproduction and can then figure it out.
Comment From: JooHyukKim
at app//org.springframework.http.converter
This could mean Guava module not rehistered at all...
Re-reading the Creator method part , List
@krritik Change ur creator parameter directly to Guava's ImmutableList and see what error throws
Comment From: cowtowncoder
@JooHyukKim You may be looking at "work-around" case: initial one does have it:
@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PRIVATE)
class MimirAssertsRelabelRules {
private ImmutableList<MimirRelabelRuleGroup> asserts;
}
same wrt GuavaModule.