Why When a Constructor Is Annotated with @JSONcreator, Its Arguments Must Be Annotated with @JSONproperty

Why when a constructor is annotated with @JsonCreator, its arguments must be annotated with @JsonProperty?

Jackson has to know in what order to pass fields from a JSON object to the constructor.
It is not possible to access parameter names in Java using reflection - that's why you have to repeat this information in annotations.

Is use of @JsonCreator necessary when using @JsonProperty?

Jackson has to know in what order to pass fields from a JSON object to the constructor. Because you have a single argument constructor, the creation works without @JsonCreator

From javadoc

Marker annotation that can be used to define constructors and factory
methods as one to use for instantiating new instances of the
associated class.

NOTE: when annotating creator methods (constructors, factory methods),
method must either be:

Single-argument constructor/factory method without JsonProperty
annotation for the argument: if so, this is so-called "delegate
creator", in which case Jackson first binds JSON into type of the
argument, and then calls creator Constructor/factory method where
every argument is annotated with either JsonProperty or JacksonInject,
to indicate name of property to bind to

Constructor annotated with Jackson @JsonCreator not being called in POJO?

After investigating some more I discovered the issue lies with the Spring Framework implementation of @JsonCreator - I removed the imports for org.springframework.cloud.cloudfoundry.com.fasterxml.jackson.annotation and replaced them with com.fasterxml.jackson.annotation. The above implementation now functions as expected.

I have been unable to find an explanation online as to why the spring version isn't working, so if anyone has any ideas please let me/ others know

Deserialize JSON with Jackson and @JsonCreator

From jackson annotations javadoc, @JsonPropertyOrder is used for serialization, not deserialization:

Annotation that can be used to define ordering (possibly partial) to use when serializing object properties.

Also, when the constructor is annotated with @JsonCreator, constructor must either be:

  • Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator.
  • Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to.

The problem is that you try to deserialize from a json list ["17", 19561093, ".", ..] to a PojoVariant. Jackson is smart but not that smart. Here he needs some help and you can help him implementing a custom deserializer. We don't wanna do that, so let's hope we can find something else.

The hint comes from the @JsonCreator javadoc (first bullet, bold text). And, because Jackson knows how to bind the list into an array of objects, we can rewrite PojoVariant constructor like this:

@JsonCreator
public PojoVariant(Object[] params) {
this.chr = (String) params[0];
this.pos = (int) params[1];
this.id = (String) params[2];
this.ref = (String) params[3];
this.alt = (String) params[4];
this.quality = (int) params[5];
this.filter = (String) params[6];
this.type = (String) params[7];
this.genotype = (String) params[8];
this.alignPos = (int) params[9];
this.basePos = (int) params[10];
this.signalPos = (int) params[11];
}

How to deserialize a class with overloaded constructors using JsonCreator

Though its not properly documented, you can only have one creator per type. You can have as many constructors as you want in your type, but only one of them should have a @JsonCreator annotation on it.

Flat JSON in Jackson for class/record/value object with one field

The combination of @JsonUnwrapped and @JsonCreator is not supported yet, so we can generate a solution like this:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.util.UUID;

public class AggregateTest {

static record AggregateId(@JsonProperty("aggregateId") UUID id) {}

static class Aggregate {

@JsonUnwrapped
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public final AggregateId _aggregateId;
public final String otherField;

@JsonCreator
public Aggregate(@JsonProperty("aggregateId") UUID aggregateId,
@JsonProperty("otherField") String otherField) {
this._aggregateId = new AggregateId(aggregateId);
this.otherField = otherField;
}
}

public static void main(String[] args) throws JsonProcessingException {
String rawJson =
"{\"aggregateId\": \"1f61aede-83dd-4049-a6ff-337887b6b807\"," +
"\"otherField\": \"İsmail Y.\"}";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
Aggregate aggregate = objectMapper
.readValue(rawJson, Aggregate.class);
System.out.println(objectMapper
.writeValueAsString(aggregate));
}
}

Here we briefly get rid of the @JsonUnwrapped field.

We get the UUID with the name aggregateId and create an AggregateId record.

Detailed explanations about it:

  • https://github.com/FasterXML/jackson-databind/issues/1467
  • https://github.com/FasterXML/jackson-databind/issues/1497


Related Topics



Leave a reply



Submit