为啥构造函数用@JsonCreator注解时,它的参数必须用@JsonProperty注解?

Posted

技术标签:

【中文标题】为啥构造函数用@JsonCreator注解时,它的参数必须用@JsonProperty注解?【英文标题】:Why when a constructor is annotated with @JsonCreator, its arguments must be annotated with @JsonProperty?为什么构造函数用@JsonCreator注解时,它的参数必须用@JsonProperty注解? 【发布时间】:2014-03-22 03:18:00 【问题描述】:

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

我不明白为什么它是必要的。能解释一下吗?

【问题讨论】:

【参考方案1】:

当我正确理解 this 时,您将默认构造函数替换为参数化构造函数,因此必须描述用于调用构造函数的 JSON 键。

【讨论】:

【参考方案2】:

正如annotation documentation中所说的,注解表明参数名称被用作属性名称,不做任何修改,但可以指定为非空值以指定不同的名称:

【讨论】:

【参考方案3】:

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

【讨论】:

这对 Java8 无效 @MariuszS 这是真的,但是这个post 解释了如何在Java8 编译器标志和Jackson 模块的帮助下摆脱无关注释。我已经测试了这种方法并且它有效。 当然,像魅力一样工作:) docs.oracle.com/javase/tutorial/reflect/member/…【参考方案4】:

因为 Java 字节码不保留方法或构造函数参数的名称。

【讨论】:

不再正确:docs.oracle.com/javase/tutorial/reflect/member/… @MariuszS 确实如此,但由于这是一个新的(非默认编译器标志),Jackson 将不得不继续支持其 @JsonProperty 注释【参考方案5】:

使用 jdk8 可以避免构造函数注释,其中编译器将可选地引入带有构造函数参数名称的元数据。然后通过jackson-module-parameter-names 模块杰克逊可以使用这个构造函数。你可以在帖子Jackson without annotations看到一个例子

The Java™ Tutorials - Obtaining Names of Method Parameters

【讨论】:

已弃用并移至jackson-modules-java8/parameter-names【参考方案6】:

Java 代码在运行时通常无法访问参数名称(因为它是由编译器删除的),因此,如果您想要该功能,您需要使用 Java 8 的内置功能或使用诸如 ParaNamer 之类的库。访问它。

因此,为了在使用 Jackson 时不必为构造函数参数使用注释,您可以使用以下两个 Jackson 模块中的任何一个:

杰克逊模块参数名称

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

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

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

javac -parameters ...

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

杰克逊模块参数

另外一个只需要您注册模块或配置注释自省(但并非如 cmets 所指出的那样)。它允许您在 1.8 之前的 Java 版本上使用无注释的构造函数参数。

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 编译器标志。你知道有什么缺点吗?【参考方案7】:

可以简单地使用 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;
  

【讨论】:

非常好的捕获,我无法找到其他方法:因此,不依赖于 Jackson API 并且不那么冗长!【参考方案8】:

只是遇到它并在某处得到答案。从 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;
    

【讨论】:

以上是关于为啥构造函数用@JsonCreator注解时,它的参数必须用@JsonProperty注解?的主要内容,如果未能解决你的问题,请参考以下文章

注解有啥作用,啥时候用注解。Java中怎么样实现注解的构造函数

为啥复制构造函数应该在 C++ 中通过引用来接受它的参数?

为啥标准不将模板构造函数视为复制构造函数?

为啥我们需要私有构造函数?

Jackson 还需要 getter 方法来使用 @JsonCreator 正确序列化 bean 属性

c#中泛型类构造函数重载赋值时为啥不接受null?对其赋空值应给怎么做?