我有一个带有私有构造函数和静态工厂的简单类。我希望该类序列化为数字,因此我使用@JsonValue
为该字段的getter注释。然而,杰克逊似乎更喜欢私有构造函数而不是静态工厂,即使我使用@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;
}
}
示例测试:
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);
}
}
答案 0 :(得分:12)
如果参数是Java标准类型,Jackson确实会使用构造函数方法覆盖JsonCreator方法。我想说这是BasicDeserializerFactory#_handleSingleArgumentConstructor方法中的一个错误。
因此,问题是如果构造函数和静态工厂方法具有常规Java类型,则构造函数具有比静态工厂方法更高的优先级。如何解决它的方法很少。
将创建者可见性级别设置为NON_PRIVATE:
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NON_PRIVATE)
class NonNegative {
第二种方法是删除静态工厂方法并使用构造函数。我将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;
}
}
另一种方法是使用@JsonIgnore注释,但你提到你不喜欢这种方法:)
更新我已经记录了一个错误:https://github.com/FasterXML/jackson-databind/issues/660
更新杰克逊错误更喜欢构造函数而非静态工厂方法已解决:https://github.com/FasterXML/jackson-databind/commit/257ae1c7a88c5ccec2882433a39c0df1de2b73aa