question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Properties naming strategy do not work with Record

See original GitHub issue

Hello,

When I try to use a @JsonNaming annotation on a record, I cannot unmarshall json to an object because a mapping exception occurs.

I use jackson 2.12.0 with JDK 15.

A Test example can be something like:

    @Test
    void tryJsonNamingOnRecord() throws Exception{
        ObjectMapper mapper=new ObjectMapper();

        @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
                record Test(String myId, String myValue){}

        var src=new Test("id", "value");
        String json=mapper.writeValueAsString(src);
        assertThat(json).contains("\"my_id\":\"id\"", "\"my_value\":\"value\"");
        var after=mapper.readValue(json, Test.class);
        assertThat(after).isEqualTo(src);
    }

The json String is generated correctly, but when unmarshalling, I got an exception

com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.lang.String field test.Tests$1Test.myValue to java.lang.String
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:274)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:623)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:611)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:634)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:193)
	at com.fasterxml.jackson.databind.deser.impl.PropertyValue$Regular.assign(PropertyValue.java:62)
	at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:211)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:520)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1390)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:195)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4591)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3546)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3514)
	at test.Tests.tryJsonNamingOnRecord(Tests.java:100)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	...
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
...
Caused by: java.lang.IllegalAccessException: Can not set final java.lang.String field test.Tests$1Test.myValue to java.lang.String
	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:793)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:190)
	... 76 more

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:30
  • Comments:22 (9 by maintainers)

github_iconTop GitHub Comments

12reactions
cowtowncodercommented, Oct 2, 2022

@cgpassante Unfortunately there is no concrete idea: problem is that it would be fixable as part of big rewrite of property introspection. But I have not found enough contiguous time to spend on tackling this (I only work on Jackson during my spare time): it could be that I might be able to do that within next month if something opened up. And yet with all the other things going on, it may be that it won’t happen this year.

What I do not want to try is a one-off hack to work around just this problem since that tends to complicate eventual rewrite.

I agree that this problem area – proper support of Java Records – is VERY important, and I try my best to find time to resolve it properly.

8reactions
jedvardssoncommented, Mar 15, 2021

Great. I will only use my fix will until 2.13. Here is a version that does not modify in place:

package com.examaple.jackson;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.deser.ValueInstantiators;
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class RecordNamingStrategyPatchModule extends SimpleModule {

    @Override
    public void setupModule(SetupContext context) {
        context.addValueInstantiators(new ValueInstantiatorsModifier());
        super.setupModule(context);
    }

    /**
     * Remove when the following issue is resolved: 
     * <a href="https://github.com/FasterXML/jackson-databind/issues/2992">Properties naming strategy do not work with Record #2992</a>
     */
    private static class ValueInstantiatorsModifier extends ValueInstantiators.Base {
        @Override
        public ValueInstantiator findValueInstantiator(
                DeserializationConfig config, BeanDescription beanDesc, ValueInstantiator defaultInstantiator
        ) {
            if (!beanDesc.getBeanClass().isRecord() || !(defaultInstantiator instanceof StdValueInstantiator) || !defaultInstantiator.canCreateFromObjectWith()) {
                return defaultInstantiator;
            }
            Map<String, BeanPropertyDefinition> map = beanDesc.findProperties().stream().collect(Collectors.toMap(p -> p.getInternalName(), Function.identity()));
            SettableBeanProperty[] renamedConstructorArgs = Arrays.stream(defaultInstantiator.getFromObjectArguments(config))
                    .map(p -> {
                        BeanPropertyDefinition prop = map.get(p.getName());
                        return prop != null ? p.withName(prop.getFullName()) : p;
                    })
                    .toArray(SettableBeanProperty[]::new);

            return new PatchedValueInstantiator((StdValueInstantiator) defaultInstantiator, renamedConstructorArgs);
        }
    }

    private static class PatchedValueInstantiator extends StdValueInstantiator {

        protected PatchedValueInstantiator(StdValueInstantiator src, SettableBeanProperty[] constructorArguments) {
            super(src);
            _constructorArguments = constructorArguments;
        }
    }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Jackson is ignoring spring.jackson.properties in my spring ...
You can config it in "application.properties" with this: spring.jackson.property-naming-strategy=com.fasterxml.jackson.databind.
Read more >
How to customize property names and values with System ...
Learn how to customize property names and values when serializing with System.Text.Json in .NET.
Read more >
Deserialize Snake Case to Camel Case With Jackson
As Java usually uses camel case field naming by convention, this makes it harder to deserialize snake case JSON. We look at some...
Read more >
PropertyNamingStrategy (jackson-databind 2.7.0 API)
In absence of a registered custom strategy, default Java property naming strategy is used, which leaves field names as is, and removes set/get/is...
Read more >
Schema Validation on Confluent Server
Starting with Confluent Platform 5.5.0, the naming strategy is associated ... and the messages we are sending do not contain schema IDs: This...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found