当使用@JsonValue对类进行反序列化时,Jackson更喜欢私有构造函数而不是@JsonCreator

Nic*_*win 9 java json jackson

我有一个带私有构造函数和静态工厂的简单类.我希望该类序列化为一个数字,所以我已经注释了该字段的getter @JsonValue.然而,杰克逊似乎更喜欢私有构造函数而不是静态工厂,即使我用静态工厂注释@JsonCreator.如果我用私有构造函数注释,它可以工作@JsonIgnore,但感觉有点偏.

我看过一些帖子声称@JsonCreator只有在参数注释时才有效@JsonProperty; 但是,对于序列化为JSON对象的对象来说似乎就是这种情况.此对象被序列化为数字,因此没有提供给注释的属性.

有什么我想念的吗?

示例类:

package com.example;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Preconditions;

public class NonNegative {
  private final double n;

  private NonNegative(double n) {
    this.n = n;
  }

  @JsonCreator
  public static NonNegative checked(double n) {
    Preconditions.checkArgument(n >= 0.0);
    return new NonNegative(n);
  }

  @JsonValue
  public double getValue() {
    return n;
  }

  @Override
  public int hashCode() {
    return Objects.hash(n);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj instanceof NonNegative) {
      NonNegative that = (NonNegative) obj;
      return Objects.equals(n, that.n);
    }
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

示例测试:

package com.example;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class NonNegativeTest {
  private static final ObjectMapper MAPPER = new ObjectMapper();

  @Test
  public void itSerializesAndDeserializes() throws Exception {
    NonNegative nonNegative = NonNegative.checked(0.5);

    assertThat(MAPPER.readValue(MAPPER.writeValueAsString(nonNegative), NonNegative.class)).isEqualTo(nonNegative);
  }

  /* This test fails. */
  @Test(expected = JsonMappingException.class)
  public void itDoesNotDeserializeANegativeNumber() throws Exception {
    MAPPER.readValue(MAPPER.writeValueAsString(-0.5), NonNegative.class);
  }
}
Run Code Online (Sandbox Code Playgroud)

Ily*_*nov 12

事实上,如果参数是Java标准类型,Jackson将使用构造方法覆盖JsonCreator方法.我想说这是BasicDeserializerFactory#_handleSingleArgumentConstructor方法中的一个错误.

因此,问题是如果构造函数和静态工厂方法具有常规Java类型,则构造函数具有比静态工厂方法更高的优先级.解决问题的方法很简单.

将创建者可见性级别设置为NON_PRIVATE:

@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NON_PRIVATE)
class NonNegative {
Run Code Online (Sandbox Code Playgroud)

第二种方法是删除静态工厂方法并使用构造函数.我将Preconditions.checkArgument移动到构造函数(它没有做太多...如果不满足条件,则抛出IllegalArgumentException):

public class NonNegative {
  private final double n;

  private NonNegative(double n) {
    Preconditions.checkArgument(n >= 0.0);
    this.n = n;
  }

  @JsonValue
  public double getValue() {
    return n;
  }
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用@JsonIgnore注释,但你提到你不喜欢这种方法:)

更新我记录了一个错误:https://github.com/FasterXML/jackson-databind/issues/660

更新杰克逊错误,更喜欢构造函数而非静态工厂方法已解决:https://github.com/FasterXML/jackson-databind/commit/257ae1c7a88c5ccec2882433a39c0df1de2b73aa