vavr-io / vavr-jackson

Jackson datatype module for Vavr
Apache License 2.0
99 stars 38 forks source link

@JsonDeserialize(contentAs = ...) is ignored ? #101

Closed densmnko closed 7 years ago

densmnko commented 7 years ago

It seems that @JsonDeserialize(contentAs = X.class) is ignored.

Here is some test fragment:

    @JavaslangEncodingEnabled
    public static class L {
        @JsonDeserialize(contentAs = X.class)
        private List<AX> xs;

        public L() {
        }

        public L(List<AX> xs) {
            this.xs = xs;
        }

        public List<AX> getXs() {
            return xs;
        }

        public void setXs(List<AX> xs) {
            this.xs = xs;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            L l = (L) o;

            return xs != null ? xs.equals(l.xs) : l.xs == null;
        }

        @Override
        public int hashCode() {
            return xs != null ? xs.hashCode() : 0;
        }
    }

    public interface  AX {
        String getaString();
        int getAnInt();
    }

    public static class X implements AX {
        String aString ;
        int anInt ;

        public X() {
        }

        public X(String aString, int anInt) {
            this.aString = aString;
            this.anInt = anInt;
        }

        public String getaString() {
            return aString;
        }

        public void setaString(String aString) {
            this.aString = aString;
        }

        public int getAnInt() {
            return anInt;
        }

        public void setAnInt(int anInt) {
            this.anInt = anInt;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            X x = (X) o;

            if (anInt != x.anInt) return false;
            return aString != null ? aString.equals(x.aString) : x.aString == null;
        }

        @Override
        public int hashCode() {
            int result = aString != null ? aString.hashCode() : 0;
            result = 31 * result + anInt;
            return result;
        }
    }

    @Test
    public void javaslang_jackson() throws IOException {
        L l = new L(List.of(new X("a", 1), new X("bbb", 42)));
        json_roundtrip_test(l, L.class);
    }

 public static  <T> T json_roundtrip_test(T value, Class<T> valueType) throws IOException {
        final String asString = JacksonMapper.jsonMapper()
                .writerWithDefaultPrettyPrinter()
                .writeValueAsString(value);
        assertNotNull(asString);
       // System.out.println(asString);
        final T value_decoded = JacksonMapper.jsonMapper().readValue(asString, valueType);
        assertEquals(value,value_decoded);
        return value_decoded;
    }

readValue fail with com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of ...$AX: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

Works fine when using java.util.List.

Using javaslang 2.0.5, jackson 2.8.5 and javaslang-encodings 0.2.0.

ruslansennov commented 7 years ago

Hi @densmnko This modified test (without javaslang-encodings) works fine:

public static <T> void json_roundtrip_test_modified(T value, Class<T> valueType) 
        throws IOException {

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaslangModule());

    String asString = mapper.writeValueAsString(value);
    assertNotNull(asString);
    System.out.println(asString);

    final T value_decoded = mapper.readValue(asString, valueType);
    assertEquals(value, value_decoded);
}

@io7m please take a look, maybe problem is in immutables-javaslang artifact

io7m commented 7 years ago

@densmnko: Can you put a minimal example in a repository somewhere that I can test? Something similar to https://github.com/io7m/immutables-javaslang-bug-20161226

densmnko commented 7 years ago

Sure, here it is https://github.com/densmnko/javaslang_encoding_bug_20170112

Using jackson.version 2.8.5 brokes the thing, using 2.6.5 is OK.

It seems a conflict is somewere around jackson-databind...

io7m commented 7 years ago

Hm, if I remove all references to immutables-javaslang, I can still reproduce the problem:

{"title":"Mr","dob":[2017,1,12],"phones":[{"number":"555-55555"},{"number":"42-424242"}],"name":"John"}

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of sample.immutables_javaslang_bug_20170112.Schema$Phone: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: {"title":"Mr","dob":[2017,1,12],"phones":[{"number":"555-55555"},{"number":"42-424242"}],"name":"John"}; line: 1, column: 43] (through reference chain: sample.immutables_javaslang_bug_20170112.PersonImpl$Json["phones"])

    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
    at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:149)
    at javaslang.jackson.datatype.deserialize.ArrayDeserializer.deserialize(ArrayDeserializer.java:57)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:499)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:101)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:178)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1194)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:314)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
    at sample.immutables_javaslang_bug_20170112.SampleTest.json_test(SampleTest.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

I did notice this, however:

conflict

It seems like that version of javaslang-jackson wants jackson-databind-2.6.5 but it's getting a much newer version after conflict resolution.

ruslansennov commented 7 years ago

Thank you guys, I'll investigate these conflicts

ruslansennov commented 7 years ago

Minimal version of jackson increased to 2.7.2 Also all tests passed with jackson 2.8.4 (see travis config)