jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

Deserializing Java record with @JsonUnwrapped annotation throws exception "can not set final"

Open jonjanisch opened this issue 2 years ago • 6 comments

I'm attempting to deserialize JSON into a simple Java record where one of the members, a simple POJO, is annotated as @JsonUnwrapped.

public record TestRecord(String name, @JsonUnwrapped Address address) {}

If I attempt to deserialize the JSON string {"name":"Bob","city":"New York","state":"NY"}, I get the exception:

    com.fasterxml.jackson.databind.JsonMappingException: Can not set final org.example.AppTest$Address field org.example.AppTest$TestRecord.address to org.example.AppTest$Address
 at [Source: UNKNOWN; byte offset: #UNKNOWN]

    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:276)
Caused by: java.lang.IllegalAccessException: Can not set final org.example.AppTest$Address field org.example.AppTest$TestRecord.address to org.example.AppTest$Address
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
    at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:79)
    at java.base/java.lang.reflect.Field.set(Field.java:799)

Version information Tested both 2.13.2 and 2.14.1 - same problem with both versions.

To Reproduce This uses JDK17 multiline string but trivial to replace with escaped string.

@Test
void should_DeserializeUnwrappedRecord() throws JsonProcessingException {

    Address address = new Address();
    address.setCity("New York");
    address.setState("NY");
    TestRecord expectedRecord = new TestRecord("Bob", address);

    String json = """
            {"name":"Bob","city":"New York","state":"NY"}""";

    JsonMapper objectMapper = new JsonMapper();
    TestRecord actualValue = objectMapper.readValue(json, TestRecord.class);
    assertEquals(expectedRecord, actualValue);
}

Additional context If I change TestRecord to a plain Java class, it works fine.

I've tried using the annotations @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) and annotating all fields with @JsonProperty to no avail.

jonjanisch avatar Jan 09 '23 16:01 jonjanisch