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

Mar*_*eln 3 java serialization jackson deserialization kotlin

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

Kotlin中的示例值对象:

data class MyValueObject(val value: String)
Run Code Online (Sandbox Code Playgroud)

或作为Java

class MyValueObject {
 private String value;

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

 public String getValue() { return value; }
}
Run Code Online (Sandbox Code Playgroud)

这些值对象必须被序列化和反序列化并且应该被序列化为“仅值”,例如。"theValue"代替"{"value":"theValue"}"

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

我知道用于序列化@JsonValue可以用来实现以上内容。

data class MyValueObject(@JsonValue val value: String)
Run Code Online (Sandbox Code Playgroud)

但是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]
Run Code Online (Sandbox Code Playgroud)

这是我使用的单元测试:

@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)
}
Run Code Online (Sandbox Code Playgroud)

是否有一种简单/通用的方法来反@JsonValue序列化这些内容,例如进行序列化?

PS:一个有效的解决方案(感谢@LppEdd)是:

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

    companion object {
        @JvmStatic
        @JsonCreator
        fun create(value: String) = MyValueObject(value)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Lpp*_*Edd 6

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

class MyValueObject {
  private String value;

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

  @JsonValue
  public String getValue() { return value; }
}
Run Code Online (Sandbox Code Playgroud)

或者,对于科特林,我想

data class MyValueObject @JsonCreator constructor(@JsonValue val value: String)
Run Code Online (Sandbox Code Playgroud)

使用@JsonCreator 不带前缀的构造方法参数时

@JsonProperty("fieldName")
Run Code Online (Sandbox Code Playgroud)

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


显然,Kotlin不喜欢自动生成的getter / setters对。
这个工作例如

class MyValueObject @JsonCreator constructor(private val value: String) {
    @JsonValue
    fun getValue() = value
}
Run Code Online (Sandbox Code Playgroud)

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

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

JacksonAnnotationIntrospector

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

解决方法是

data class MyValueObject @JsonCreator constructor(@JvmField @JsonValue val value: String)
Run Code Online (Sandbox Code Playgroud)

甚至更好

data class MyValueObject @JsonCreator constructor(@get:JsonValue val value: String)
Run Code Online (Sandbox Code Playgroud)

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

data class MyValueObject(@get:JsonValue val value: String)
Run Code Online (Sandbox Code Playgroud)

但是,这似乎没有记录。如果有人指出在哪里描述了这种行为,那将是非常棒的!