jackson-databind: Exception when deserialization uses a record with a constructor property with access=READ_ONLY
Search before asking
- I searched in the issues and found nothing similar.
Describe the bug
When deserialization uses a record with a constructor property with access=READ_ONLY
, the following exception is thrown:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `com.company.common.vo.RuleVO`: Argument #1 of constructor [constructor for `com.company.common.vo.RuleVO` (4 args), annotations: [null] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
at [Source: (BufferedReader); line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadTypeDefinition(DeserializationContext.java:1893)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addImplicitConstructorCreators(BasicDeserializerFactory.java:602)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:301)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:222)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:262)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:151)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:415)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:350)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:621)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:188)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:867)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:659)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4956)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4826)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3801)
A sample record is provided bellow.
Version Information
2.15.2
Reproduction
Sample record with the issue:
public record RuleVO(
@JsonIgnore
Integer id,
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
Integer order,
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String name,
String description,
) {}
If I remove the JsonProperty.Access.READ_ONLY
from order field, a new exception is thrown indicating the argument with the issue is ...: Argument #2 of constructor [constructor for...
, that is, the other field with READ_ONLY.
The object mapper is:
public static ObjectMapper customObjectMapper() {
return JsonMapper.builder()
.addModule(new JavaTimeModule())
.disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();
}
I also tried with a plain object mapper JsonMapper.builder().build()
without customizations and the error is still there.
Expected behavior
In 2.14.3, it works perfectly.
Additional context
I have tested the application with 2.15.3-SNAPSHOT and 2.16.0-SNAPSHOT and the error is thrown too.
About this issue
- Original URL
- State: open
- Created 9 months ago
- Comments: 21 (12 by maintainers)
Commits related to this issue
- Add test cases for issue #4119. — committed to yihtserns/jackson-databind by yihtserns 9 months ago
- Add test cases for issue #4119. — committed to yihtserns/jackson-databind by yihtserns 9 months ago
- Add test cases for issue #4119. — committed to yihtserns/jackson-databind by yihtserns 9 months ago
- Add test cases for issue #4119. — committed to yihtserns/jackson-databind by yihtserns 9 months ago
- Add test cases for issue #4119. — committed to yihtserns/jackson-databind by yihtserns 9 months ago
- Add test cases for issue #4119. — committed to yihtserns/jackson-databind by yihtserns 9 months ago
One of the benefits of
record
is that you have first-class support for getting the names (Class#getRecordComponents
), so losing that feels kinda wrong.I’m sorry @cowtowncoder it was my mistake. Will edit the message!
Deciding factor is whether it SHOULD HAVE worked in a way it did – or was that a bug wrt semantics. Just because earlier version behaves in certain way does not automatically mean it was how it should have. Even in cases where users found that behavior useful. It is of course very unfortunate if this is the case – what seems like useful Feature is seen by maintainers as a bug.
As to “a lot of apps”, that is hard to estimate. I do not know, but I am bit surprised if it turns out
READ_ONLY
option is widely used. There have been some bug reports but I never it was a commonly used feature, fwtw. And we do not really have any usage metrics to check: rate of bug reports is not a strong indicator either.But as to alternatives, plain
@JsonIgnore
on argument could work, but probably requires an explicit getter with@JsonProperty
annotation.Another, similarly awkward possibility would be to add a bogus setter that does nothing: it’d get called but value would be ignored. That setter might require
@JsonProperty
(or@JsonSetter
).Or, come to think of it, an explicit constructor that calls
super()
with same arguments but not passing read-only argument?Writing all of this, I am starting to think maybe we should allow
READ_ONLY
to mean “partial ignore”, given how awkward work-arounds are for Record types.So I would consider PR to support it.
I understand that the error message is not the best one, but taking into account that versions < 2.14.x were working with READ_ONLY with much probability a lot of apps will break with 2.15.x, so what is the alternative to use READ_ONLY or something similar in records to not deserialize some fields?
Yes, it is not possible for jackson to deserialize an object if the creator/constructor would have to set a READ_ONLY property. That is reasonable behavior, it’s just the error message that is awful.
imo it should still throw an exception, just a better one.
oh duh, the issue is much more simple. since you define the property as READ_ONLY, it can’t be deserialized. the error could be better but there is no functional change needed here imo.