在Jackson中,是否有一种通用的方法来反序列化单值值对象(没有自定义反序列化器)

时间:2019-04-03 10:14:24

标签: java serialization kotlin jackson deserialization

我们使用很多具有单个值的值对象。对于(反)序列化,我们将Jackson与kotlin模块一起使用。

Kotlin中的示例值对象:

data class MyValueObject(val value: String)

或作为Java

class MyValueObject {
 private String value;

 public MyValueObject(String value) { this.value = value; }

 public String getValue() { return value; }
}

这些值对象必须进行序列化和反序列化,并且应序列化为“仅值”,例如。 "theValue"而不是"{"value":"theValue"}"

我想避免为数十个值对象编写自定义序列化器/反序列化器。

我知道对于序列化@JsonValue可以用来实现上述目的。

data class MyValueObject(@JsonValue val value: String)

但是JSON“” theValue“”(以上序列化的String-Literal)不能反序列化为MyValueObject。它导致以下异常:

com.fasterxml.jackson.databind.exc.MismatchedInputException: 
Cannot construct instance of `[...].MyValueObject` (although at least one Creator exists): 
no String-argument constructor/factory method to deserialize from String value ('test')
 at [Source: (String)""test""; line: 1, column: 1]

这是我使用的单元测试:

@Test
fun testSerialize() {
    val objectMapper = ObjectMapper()
        .registerModule(Jdk8Module())
        .registerModule(KotlinModule())
    val test = MyValueObject("test")
    val json = objectMapper.writeValueAsString(test)
    println(json)
    objectMapper.readValue<MyValueObject>(json)
}

是否有一种简单/通用的方法来反序列化诸如@JsonValue进行序列化?

P.S .:一种可行的解决方案(由于@LppEdd)是:

data class MyValueObject (@JsonValue val value: String) {

    companion object {
        @JvmStatic
        @JsonCreator
        fun create(value: String) = MyValueObject(value)
    }
}

但这很冗长。在构造函数上注释的@JsonCreator对我不起作用(请参阅我对@LppEdd答案的评论)

1 个答案:

答案 0 :(得分:3)

当然,这很简单。
只需将@JsonCreator添加到构造函数即可。

class MyValueObject {
  private String value;

  @JsonCreator
  public MyValueObject(String value) { this.value = value; }

  @JsonValue
  public String getValue() { return value; }
}

或者我想让Kotlin拥有

data class MyValueObject @JsonCreator constructor(@JsonValue val value: String)

在使用@JsonCreator给构造函数参数加上前缀

@JsonProperty("fieldName")

您告诉Jackson传递整个JSON字符串,在您的情况下,这只是一个“原始”值。


显然Kotlin不喜欢自动生成的getter / setter对。
例如,这有效

class MyValueObject @JsonCreator constructor(private val value: String) {
    @JsonValue
    fun getValue() = value
}

另外,经过一些调试,我发现问题出在这里

@Override
public Boolean hasAsValue(Annotated a) {
    JsonValue ann = _findAnnotation(a, JsonValue.class);
    if (ann == null) {
        return null;
    }
    return ann.value();
}

JacksonAnnotationIntrospector

我不知道为什么,但杰克逊仍然在 public 字段或 public 吸气剂上寻找@JsonValue注释。 Kotlin将注释放在私有字段上,因此Jackson找不到它。

解决方案是

data class MyValueObject @JsonCreator constructor(@JvmField @JsonValue val value: String)

甚至更好

data class MyValueObject @JsonCreator constructor(@get:JsonValue val value: String)

正如马克·冯·伦特恩(Marc von Renteln)在评论中所写,您也可以省略@JsonCreator

data class MyValueObject(@get:JsonValue val value: String)

但是,这似乎没有记录。如果有人可以指出这种行为的描述位置,那就太棒了!