155

In Jackson, when you annotate a constructor with @JsonCreator, you must annotate its arguments with @JsonProperty. So this constructor

public Point(double x, double y) {
    this.x = x;
    this.y = y;
}

becomes this:

@JsonCreator
public Point(@JsonProperty("x") double x, @JsonProperty("y") double y) {
    this.x = x;
    this.y = y;
}

I don't understand why it's necessary. Can you please explain?

Ori Popowski
  • 10,432
  • 15
  • 57
  • 79

8 Answers8

147

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.

Lukasz Wiktor
  • 19,644
  • 5
  • 69
  • 82
  • 12
    This is not valid for Java8 – MariuszS Sep 06 '15 at 16:43
  • 14
    @MariuszS That is true but this [post](http://stackoverflow.com/a/32272061/271149) explains how to get rid of extraneous annotations with the help of Java8 compiler flag and a Jackson module. I've tested the approach and it works. – quantum Sep 21 '15 at 17:46
  • Ofcourse, works like a charm :) https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html – MariuszS Sep 21 '15 at 18:05
68

Parameter names are normally not accessible by the Java code at runtime (because it's drop by the compiler), so if you want that functionality you need to either use Java 8's built-in functionality or use a library such as ParaNamer in order to gain access to it.

So in order to not having to utilize annotations for the constructor arguments when using Jackson, you can make use of either of these 2 Jackson modules:

jackson-module-parameter-names

This module allows you to get annotation-free constructor arguments when using Java 8. In order to use it you first need to register the module:

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

Then compile your code using the -parameters flag:

javac -parameters ...

Link: https://github.com/FasterXML/jackson-modules-java8/tree/master/parameter-names

jackson-module-paranamer

This other one simply requires you to register the module or configure an annotation introspection (but not both as pointed out by the comments). It allows you to use annotation-free constructor arguments on versions of Java prior to 1.8.

ObjectMapper mapper = new ObjectMapper();
// either via module
mapper.registerModule(new ParanamerModule());
// or by directly assigning annotation introspector (but not both!)
mapper.setAnnotationIntrospector(new ParanamerOnJacksonAnnotationIntrospector());

Link: https://github.com/FasterXML/jackson-modules-base/tree/master/paranamer

Rodrigo Quesada
  • 1,460
  • 1
  • 14
  • 20
  • Paranamer module seems to be much better than ParameterNames: it does not require Java 8 and does not require the `-parameters` compiler flag. Are you aware of any downside? – Mauro Molinari Mar 16 '21 at 12:52
32

It is possible to avoid constructor annotations with jdk8 where optionally the compiler will introduce metadata with the names of the constructor parameters. Then with jackson-module-parameter-names module Jackson can use this constructor. You can see an example at post Jackson without annotations

MariuszS
  • 30,646
  • 12
  • 114
  • 155
Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
23

One can simply use java.bean.ConstructorProperties annotation - it's much less verbose and Jackson also accepts it. For example :

  import java.beans.ConstructorProperties;

  @ConstructorProperties({"answer","closed","language","interface","operation"})
  public DialogueOutput(String answer, boolean closed, String language, String anInterface, String operation) {
    this.answer = answer;
    this.closed = closed;
    this.language = language;
    this.anInterface = anInterface;
    this.operation = operation;
  }
letowianka
  • 444
  • 1
  • 4
  • 9
7

Because Java bytecode does not retain the names of method or constructor arguments.

lcfd
  • 1,326
  • 10
  • 12
  • Not true anymore: https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html – MariuszS Sep 21 '15 at 18:07
  • 1
    @MariuszS indeed, but since this is a new (and non-default compiler flag), Jackson will have to continue to to support its `@JsonProperty` annotation – lcfd Dec 26 '16 at 21:05
5

When I understand this correctly, you replace the default constructor with a parameterized one and therefore have to describe the JSON keys which are used to call the constructor with.

Smutje
  • 17,733
  • 4
  • 24
  • 41
3

As precised in the annotation documentation, the annotation indicates that the argument name is used as the property name without any modifications, but it can be specified to non-empty value to specify different name:

Guy Bouallet
  • 2,099
  • 11
  • 16
-1

Just come across it and got an answer somewhere. you can use below annotation since 2.7.0

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Point {
    final private double x;
    final private double y;

    @ConstructorProperties({"x", "y"})
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}
Andrew
  • 784
  • 9
  • 15