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答案的评论)
当然,这很简单。
只需添加@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)
但是,这似乎没有记录。如果有人指出在哪里描述了这种行为,那将是非常棒的!