为什么在构造函数上使用@JsonCreator注解时,它的参数必须用@JsonProperty进行注解?

156

在Jackson中,当您使用@JsonCreator注释构造函数时,必须使用@JsonProperty注释其参数。因此,这个构造函数

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

变成这样:

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

我不明白为什么这是必须的。你可以解释一下吗?

8个回答

149

Jackson需要知道从JSON对象传递字段到构造函数的顺序。在Java中,无法使用反射访问参数名称,这就是为什么您必须在注释中重复此信息的原因。


12
这不适用于Java8。 - MariuszS
14
这是真的,但是这个帖子解释了如何通过Java8编译器标志和Jackson模块来消除多余的注释。我已经测试过这种方法,它是可行的。 - quantum
当然,运行得很好 :) https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html - MariuszS

68

通常情况下,Java代码在运行时无法访问参数名称(因为编译器会将其删除),因此如果您需要该功能,则需要使用Java 8的内置功能或使用像ParaNamer这样的库来获取访问权限。

因此,为了在使用Jackson时不必使用注释来处理构造函数参数,您可以使用以下两个Jackson模块之一:

jackson-module-parameter-names

该模块允许您在使用Java 8时获得无注释的构造函数参数。要使用它,您需要首先注册该模块:

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

然后使用-parameters标志编译您的代码:

javac -parameters ...

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

jackson-module-paranamer

另一个模块只需要您注册该模块或配置注释内省(但评论中指出不需要同时进行),它允许您在Java 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());

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


Paranamer模块似乎比ParameterNames更好:它不需要Java 8,也不需要-parameters编译器标志。您是否知道任何缺点? - Mauro Molinari

32

已弃用并移至jackson-modules-java8/parameter-names - naXa stands with Ukraine

23

一个简单的方式是使用java.bean.ConstructorProperties注解 - 这样更加简洁,Jackson也支持它。例如:

  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;
  }

2
非常好的发现,否则我找不出来:所以,不依赖于Jackson API,而且更加简洁! - Mauro Molinari

7

由于Java字节码中不保存方法或构造函数参数的名称。


不再正确:https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html - MariuszS
1
@MariuszS 的确如此,但由于这是一个新的(非默认)编译器标志,Jackson 将不得不继续支持其 @JsonProperty 注释。 - lcfd

5

如果我理解正确,您需要用带参数的构造函数替换默认构造函数,因此必须描述用于调用构造函数的JSON键。


3

注释文档中所述,该注释表示参数名称作为属性名称使用而没有任何修改,但可以指定非空值以指定不同的名称:


-1

刚刚遇到这个问题,在某处找到了答案。自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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接