Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonProperty in @JsonCreator is conflicting with POJOs getters/attributes @JsonProperty - used to work with 1.9.11 #541

Closed
fabienrenaud opened this issue Sep 12, 2014 · 12 comments
Milestone

Comments

@fabienrenaud
Copy link

Note: See my second post below for the actual problem and repro.

Example of POJO:

class Foo {
    @JsonProperty("s")
    private String str;

    Foo() {

    }

    Foo(String s) {
        this.str = str;
    }

    public String getStr() {
        return str;
    }
}

and this doesn't help:

        mapper.disable(
            MapperFeature.AUTO_DETECT_CREATORS,
            MapperFeature.AUTO_DETECT_FIELDS,
            MapperFeature.AUTO_DETECT_GETTERS,
            MapperFeature.AUTO_DETECT_IS_GETTERS,
            MapperFeature.AUTO_DETECT_SETTERS,
            MapperFeature.USE_GETTERS_AS_SETTERS
        );

Even disabling DEFAULT_VIEW_INCLUSION doesn't help.

Give an exception like:
java.lang.IllegalStateException: Conflicting/ambiguous property name definitions (implicit name 'rewards'): found multiple explicit names: [s, str], but also implicit accessor: [method Foo#getStr(0 params)][visible=true,ignore=false,explicitName=false]

@fabienrenaud fabienrenaud changed the title POJOBuilder ignores disabled MapperFeature.AUTO_DETECT_* leading to NPE POJOBuilder ignores disabled MapperFeature.AUTO_DETECT_* leading to IllegalStateException Sep 12, 2014
@cowtowncoder
Copy link
Member

I don't think example above is complete: it won't reproduce the reported issue. Would it be possible to include more complete example to show what is actually happening?
For what it is worth, DEFAULT_VIEW_INCLUSION is not relevant here (it only affects inclusion in JSON Views, when serializing, which occurs later than property introspection).

Exception suggests that there is an ambiguity in resolving what is the actual name to use, and that there are multiple explicit annotations giving conflicting names to things that are linked together be implicit names.

@fabienrenaud fabienrenaud changed the title POJOBuilder ignores disabled MapperFeature.AUTO_DETECT_* leading to IllegalStateException POJOBuilder ignores disabled MapperFeature.AUTO_DETECT_* leading to IllegalStateException - used to work with 1.9.11 Sep 12, 2014
@fabienrenaud fabienrenaud changed the title POJOBuilder ignores disabled MapperFeature.AUTO_DETECT_* leading to IllegalStateException - used to work with 1.9.11 @JsonProperty in @JsonCreator is conflicting with POJOs getters/attributes @JsonProperty - used to work with 1.9.11 Sep 12, 2014
@fabienrenaud
Copy link
Author

Full executable repro :)

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class JsonBug2 {

    public static void main(String[] args) throws IOException {
        final ObjectMapper mapper = new ObjectMapper();

        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        mapper.disable(
                MapperFeature.AUTO_DETECT_CREATORS,
                MapperFeature.AUTO_DETECT_FIELDS,
                MapperFeature.AUTO_DETECT_GETTERS,
                MapperFeature.AUTO_DETECT_IS_GETTERS,
                MapperFeature.AUTO_DETECT_SETTERS,
                MapperFeature.USE_GETTERS_AS_SETTERS
        );

        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        Foo data = mapper.readValue("{\"str\":\"the string\"}", Foo.class);
        if (data == null) {
            throw new IllegalStateException("data is null");
        }
        if (!"the string".equals(data.str)) {
            throw new IllegalStateException("bad value for data.str");
        }
    }

    private static final class Foo {

        private String str;

        @JsonCreator
        public Foo(@JsonProperty("str") String str) {
            this.str = str;
        }

        @JsonProperty("s")
        public String getStr() {
            return str;
        }

    }
}

Executing the above code will give me the following exception:

Exception in thread "main" java.lang.IllegalStateException: Conflicting/ambiguous property name definitions (implicit name 'str'): found multiple explicit names: [str, s], but also implicit accessor: [field JsonBug2$Foo#str][visible=false,ignore=false,explicitName=false]
at com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder._explode(POJOPropertyBuilder.java:873)
at com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder.explode(POJOPropertyBuilder.java:857)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._renameProperties(POJOPropertiesCollector.java:753)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collect(POJOPropertiesCollector.java:246)
at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.collectProperties(BasicClassIntrospector.java:142)
at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:81)
at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forDeserialization(BasicClassIntrospector.java:11)
at com.fasterxml.jackson.databind.DeserializationConfig.introspect(DeserializationConfig.java:550)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:323)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:261)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:241)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:381)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3154)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3047)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2146)
at JsonBug2.main(JsonBug2.java:28)

@cowtowncoder
Copy link
Member

Thanks! I will have to think about this a bit, to figure out whether this works as expected, or if it is a bug.
And even if "works as expected" it could still be something to improve upon.

The main question here is whether implicit name of 'str' creator argument is known; it appears it might be, because that's what is indicates as ambiguous (creator parameter "renamed" as 'str'; getter-induced property renamed as "s"). But then again, unless field is detected, things should be fine, because all accessors are explicitly renamed, and no ambiguity should exist.

So I guess it looks like this could be considered a bug.

One last thing: is this with 2.4.2? I am asking since there have been a few fixes since 2.3.

@fabienrenaud
Copy link
Author

Yes, this is version 2.4.2.

The feature is basically meant to reformat your json, likely to condense the keys.
For example, a json file that had to be hand written or written in a human readable format would use the @JsonCreator to initialize the class...
Then annotations on attributes or getters would be used to generate the condensed output.

What's annoying is that it used to work with 1.9.11.

And this is a totally blocking issue for me. Impossible to upgrade.

cowtowncoder added a commit that referenced this issue Sep 16, 2014
@cowtowncoder cowtowncoder added this to the 2.4.3 milestone Sep 16, 2014
@cowtowncoder
Copy link
Member

Yes, I think the visibility (or rather, lack thereof) of field str was not properly considered here.
Fixed so that test passes (and no new test failures caused).

cowtowncoder added a commit that referenced this issue Sep 17, 2014
@fabienrenaud
Copy link
Author

I'm confused. What is the milestone for this? 2.4.3 or 2.5?
Edit: I don't see it fixed in 2.4.3.

@cowtowncoder
Copy link
Member

It should be included in 2.4.3, as per release notes. Are there failing unit tests to show what is not working?

@fabienrenaud
Copy link
Author

I tried to upgrade to 2.4.3 yesterday and i still had some UT failing around that 'strange' @JsonCreator...
Gonna try to find a repro example with 2.4.3 to post here.

@fabienrenaud
Copy link
Author

Repro:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;

public class JacksonCreatorTest {

    public static void main(String[] args) throws IOException {
        new JacksonCreatorTest().run();
    }
    private final ObjectMapper mapper;

    JacksonCreatorTest() {
        mapper = new ObjectMapper();

        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.disable(
                MapperFeature.AUTO_DETECT_CREATORS,
                MapperFeature.AUTO_DETECT_FIELDS,
                MapperFeature.AUTO_DETECT_GETTERS,
                MapperFeature.AUTO_DETECT_IS_GETTERS,
                MapperFeature.AUTO_DETECT_SETTERS,
                MapperFeature.USE_GETTERS_AS_SETTERS
        );
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    public void run() throws IOException {
        Foo obj = mapper.readValue(JSON, Foo.class);
    }

    public static final class Foo {

        @JsonProperty("foo")
        private Map<Integer, Bar> foo;
        @JsonProperty("anumber")
        private long anumber;

        //Default values
        public Foo() {
            foo = Collections.emptyMap();
            anumber = 0;
        }

        public Map<Integer, Bar> getFoo() {
            return foo;
        }

        public long getAnumber() {
            return anumber;
        }
    }

    public static final class Bar {

        private final long p;
        private final List<String> stuff;

        @JsonCreator
        public Bar(@JsonProperty("p") long p, @JsonProperty("stuff") List<String> stuff) {
            this.p = p;
            this.stuff = stuff;
        }

        @JsonProperty("s")
        public List<String> getStuff() {
            return stuff;
        }

        @JsonProperty("stuff")
        private List<String> getStuffDeprecated() {
            return stuff;
        }

        public long getP() {
            return p;
        }
    }

    private static final String JSON = "{\n"
            + "    \"foo\": {\n"
            + "        \"0\": {\n"
            + "            \"p\": 0,\n"
            + "            \"stuff\": [\n"
            + "              \"a\", \"b\" \n"
            + "            ]   \n"
            + "        },  \n"
            + "        \"1\": {\n"
            + "            \"p\": 1000,\n"
            + "            \"stuff\": [\n"
            + "              \"c\", \"d\" \n"
            + "            ]   \n"
            + "        },  \n"
            + "        \"2\": {\n"
            + "            \"p\": 2000,\n"
            + "            \"stuff\": [\n"
            + "            ]   \n"
            + "        },  \n"
            + "    }  \n"
            + "    \"anumber\": 25385874\n"
            + "}";

}

This works just fine with jackson 1.9.11
With jackson 1.9.11:

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;

public class Jackson1CreatorTest {

    public static void main(String[] args) throws IOException {
        new Jackson1CreatorTest().run();
    }
    private final ObjectMapper mapper;

    Jackson1CreatorTest() {
        mapper = new ObjectMapper();
        mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(DeserializationConfig.Feature.USE_GETTERS_AS_SETTERS, false);
        mapper.configure(DeserializationConfig.Feature.AUTO_DETECT_CREATORS, false);
        mapper.configure(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, false);
        mapper.configure(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, false);

        mapper.configure(SerializationConfig.Feature.AUTO_DETECT_GETTERS, false);
        mapper.configure(SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS, false);

        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
    }

    public void run() throws IOException {
        Foo obj = mapper.readValue(JSON, Foo.class);
    }

    public static final class Foo {

        @JsonProperty("foo")
        private Map<Integer, Bar> foo;
        @JsonProperty("anumber")
        private long anumber;

        //Default values
        public Foo() {
            foo = Collections.emptyMap();
            anumber = 0;
        }

        public Map<Integer, Bar> getFoo() {
            return foo;
        }

        public long getAnumber() {
            return anumber;
        }
    }

    public static final class Bar {

        private final long p;
        private final List<String> stuff;

        @JsonCreator
        public Bar(@JsonProperty("p") long p, @JsonProperty("stuff") List<String> stuff) {
            this.p = p;
            this.stuff = stuff;
        }

        @JsonProperty("s")
        public List<String> getStuff() {
            return stuff;
        }

        @JsonProperty("stuff")
        private List<String> getStuffDeprecated() {
            return stuff;
        }

        public long getP() {
            return p;
        }
    }

    private static final String JSON = "{\n"
            + "    \"foo\": {\n"
            + "        \"0\": {\n"
            + "            \"p\": 0,\n"
            + "            \"stuff\": [\n"
            + "              \"a\", \"b\" \n"
            + "            ]   \n"
            + "        },  \n"
            + "        \"1\": {\n"
            + "            \"p\": 1000,\n"
            + "            \"stuff\": [\n"
            + "              \"c\", \"d\" \n"
            + "            ]   \n"
            + "        },  \n"
            + "        \"2\": {\n"
            + "            \"p\": 2000,\n"
            + "            \"stuff\": [\n"
            + "            ]   \n"
            + "        }  \n"
            + "    },  \n"
            + "    \"anumber\": 25385874\n"
            + "}";

}

And this is the exception i get with jackson 2:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class test.JacksonCreatorTest$Bar]: can not instantiate from JSON object (need to add/enable type information?)
 at [Source: {
    "foo": {
        "0": {
            "p": 0,
            "stuff": [
              "a", "b" 
            ]   
        },  
        "1": {
            "p": 1000,
            "stuff": [
              "c", "d" 
            ]   
        },  
        "2": {
            "p": 2000,
            "stuff": [
            ]   
        },  
    },  
    "anumber": 25385874
}; line: 4, column: 13] (through reference chain: test.Foo["foo"]->java.util.LinkedHashMap["0"])
        at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1063)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:264)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:124)
        at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBind(MapDeserializer.java:395)
        at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:314)
        at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:26)
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:538)
        at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:106)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:238)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3051)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2146)
        at test.JacksonCreatorTest.run(JacksonCreatorTest.java:39)
        at test.JacksonCreatorTest.main(JacksonCreatorTest.java:18)

cowtowncoder added a commit that referenced this issue Nov 25, 2014
@cowtowncoder
Copy link
Member

For me your test case actually passes for 2.4 branch (and master as well). I don't know if there's something in 2.4.4 that 2.4.3 is missing; or could it be that somehow you are running tests against an earlier version?

@fabienrenaud
Copy link
Author

I used the jar which is maven central repository, version 2.4.3. It didn't work with this one.

@Limraj
Copy link

Limraj commented Aug 14, 2018

Define two ObjectMappers, to Serialize and Deserialize

@Configuration
class InfoRepoConfiguration {

    @Bean
    @Primary
    ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.addMixIn(InfoRepo.class, InfoRepoPropertyJson.class);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        if(!objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT))
            objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        return objectMapper;
    }

    @Bean
    ObjectMapper objectMapperDeserialize() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.addMixIn(InfoRepo.class, InfoRepoPropertyJsonDeserialize.class);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
}

public interface InfoRepoPropertyJson {

    @JsonProperty("fullName")
    String getFullName();

    @JsonProperty("full_name")
    void setFullName(String fullName);

}

public interface InfoRepoPropertyJsonDeserialize {

    @JsonProperty("full_name")
    String getFullName();

    @JsonProperty("fullName")
    void setFullName(String fullName);
}

public class InfoRepo {
    private String fullName;
    (...getter,setter)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants