Skip to content

Creator lookup fails with InvalidDefinitionException for conflict between single-double/single-Double arg constructor #3062

@cdadac

Description

@cdadac

Describe the bug

We are using Jackson to (de)serialize POJO's representing our public API in a message oriented middleware (Apache Artemis).
We are using JsonProperty annotations to explicitly annotate getters/setters for properties to use during (de)serialization and each POJO defines at least one public no-arg constructor to use for object creation upon deserialization.
Some POJO's also include additional constructors for convenience reasons, which are not intended to be used by Jackson during deserialization.
After upgrading to 2.12.1 deserialization fails from some of those classes with the following Exception:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Conflicting from-double creators: already had implicitly discovered creator [constructor for DecVector (1 arg), annotations: [null], encountered another: [constructor for DecVector (1 arg), annotations: [null]
 at [Source: (String)"{"v":[1.0,2.0,3.0]}"; line: 1, column: 1]
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:268)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:414)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
	at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
	at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4733)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4594)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516)

The Exception is thrown by method _reportDuplicateCreator in the CreatorCollector class (line 335) which was added in 2.12

Version information

problem occurs in 2.12.1
after upgrade from 2.10.4

To Reproduce

The following code snippet shows a (simplfied) version of one of the POJOs where this problem occurs:

@JsonIgnoreProperties(ignoreUnknown = true) 
public class DecVector {

  private List<Double> elems;
  
  @JsonCreator
  public DecVector() {
    super();
  }
  
  public DecVector(double[]elems) {
    this.elems = new ArrayList<>(elems.length);
    for (double e : elems) {
      this.elems.add(Double.valueOf(e));
    }
  }
  
  public DecVector(Double[]elems) {
    this.elems = Arrays.asList(elems);
  }
  
  public DecVector(List<Double> elems) {
    this.elems = elems;
  }
  
  public DecVector(double elem) {
    this.elems = new ArrayList<>(1);
    this.elems.add(Double.valueOf(elem));
  }
  
  public DecVector(Double elem) {
    this.elems = Arrays.asList(elem);
  }
  
  @JsonProperty(required = false, value = "v", index = 1)
  @JsonInclude(value = Include.NON_NULL)
  public List<Double> getValues() {
    return elems;
  }
  public void setValues(List<Double> list) {
    this.elems = list;
  }
}

The following simple test case (using a vanilla ObjectMapper instance) works fine in 2.10.4 but throws the Exception mentioned above in 2.12.1:

  @Test
  public void testMultipleConstructor() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    DecVector vector = new DecVector(new double[] {1,2,3} );
    String result = mapper.writeValueAsString(vector);
    DecVector deser = mapper.readValue(result, DecVector.class);
    assertEquals(vector.getValues(), deser.getValues());
  }

The explicit JsonCreator annotation in the example above was added for testing purposes (with 2.10.4 we did not explicitly annotate constructors at all!) but it makes no difference if the default constructor is annotated or not.

Expected behavior

In case multiple constructors are present Jackson should default to using the parameterless default constructor if no other creator is explicitly annotated.
In case one constructor is explicitly annotated using JsonCreator Jackson should use the annotated constructor instead.

Additional context

I think the example above represents a a rather common/basic use case for (de)serialization using Jackson and should work out of the box without additional configuration.
Per default we use JSON as target format for serialized objects, however we also support Protobuf as message format, therefore the JsonProperty annotation in the example also specifies an explicit index.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions