skip to Main Content

I’m trying to deserialize a YearWeek from threetenextra library from a json scalar string using jackson with the following mix in:

import org.threeten.extra.YearWeek;
import com.fasterxml.jackson.annotation.JsonCreator;

public class YearWeekMixIn {
    @JsonCreator
    public static YearWeek parse(String value) {
        return YearWeek.parse(value);
    }
}

But the following test fails:

public class YearWeekMixInTest {

    public record Foo(YearWeek yearWeek) {}
    
    @Test
    public void foo() throws JsonMappingException, JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.addMixIn(YearWeek.class, YearWeekMixIn.class);
        
        objectMapper.readValue("{"yearWeek": "2011-W01"}", Foo.class);
    }
}

I get the following exception:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.threeten.extra.YearWeek (no Creators, like default constructor, exist): no String-argument constructor/factory method to deserialize from String value (‘2011-W01’)

What is missing in the mix in to let jackson know it should use the annotated method in the mixin for deserialization?

2

Answers


  1. Jackson’s mixin doesn’t support add outside factory method.
    So I think I have three other solutions for you.

    https://reachmnadeem.wordpress.com/2016/09/23/jackson-mixin-to-the-rescue/ maybe be help you understand the key point

    the doc’s key is the 3.
    as if the ”target class” had all annotations that the ”mix-in” class has (for purposes of configuring serialization / deserialization)
    in the mixin, it associate the annotations(it’s not as the entry as identifier), it like aspect-oriented, but actutally it isn’t aspect-oriented ehence, it just attach extra meta data to the target class

    solution1(kept all method signature consistent)

        class YearWeekMixIn {
    
            @JsonCreator
            public static YearWeek parse(CharSequence text) {
                return null;
            }
        }
    

    solution2(using StdConverter, and there is no need to config ObjectMapper)

    class YearWeekConverter extends StdConverter<String, YearWeek> {
        @Override
        public YearWeek convert(String s) {
            return YearWeek.parse(s);
        }
    }
    
    record Foo(@JsonDeserialize(converter = YearWeekConverter.class) YearWeek yearWeek) {}
    

    solution3(using StdDeserializer)

    class YearWeekDeserializer extends StdDeserializer<YearWeek> {
    
        public YearWeekDeserializer() {
            this(null);
        }
    
        public YearWeekDeserializer(Class<?> vc) {
            super(vc);
        }
    
        @Override
        public YearWeek deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            final String s = jp.readValueAs(String.class);
            return YearWeek.parse(s);
        }
    }
    
    public class YearWeekMixInTest {
    
        public record Foo(YearWeek yearWeek) {}
        
        @Test
        public void foo() throws JsonMappingException, JsonProcessingException {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.addMixIn(YearWeek.class, YearWeekMixIn.class);
    
            // add module
            SimpleModule module = new SimpleModule();
            module.addDeserializer(YearWeek.class, new YearWeekDeserializer());
            objectMapper.registerModule(module);
    
            objectMapper.readValue("{"yearWeek": "2011-W01"}", Foo.class);
        }
    }
    
    Login or Signup to reply.
  2. MixIns are used to inject annotations into methods that already exist in the class being mixed into, not to provide new implementations and not to provide custom factory methods. The signature of the mixed in method needs to match exactly with the method in the actual class. In this case, the signatures don’t match as the parse method in YearWeek class accepts a CharSequence whereas the MixIn’s parse method accepts a String. Matching the method signatures would make this work. The MixIn class could look something like the below:

    import org.threeten.extra.YearWeek;
    import com.fasterxml.jackson.annotation.JsonCreator;
    
    public abstract class YearWeekMixIn {
        @JsonCreator
        public static YearWeek parse(CharSequence value) {
            return null;
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search