提问人:vatbub 提问时间:11/17/2023 更新时间:11/17/2023 访问量:21
将 Number 隐式转换为更复杂的类型
Implicitly convert a Number to a more complex type
问:
在我的项目中,我曾经有过类似的:data class
data class SampleClassThatContainsALength(val length: Double)
使用 ,将序列化为 。jackson-databind
SampleClassThatContainsALength(0.025)
{"length":0.025}
在整个项目中,长度被隐含地假定为米,但为了减少歧义,我实现了一个类,它负责单位:Length
data class Length(val m: Double) : Comparable<Length> {
@get:JsonIgnore
val km: Double get() = m / 1e3
@get:JsonIgnore
val cm: Double get() = m / 1e-2
@get:JsonIgnore
val mm: Double get() = m / 1e-3
// More conversions to other units...
operator fun plus(other: Length) = (this.m + other.m).m
operator fun minus(other: Length) = (this.m - other.m).m
operator fun unaryMinus() = (-this.m).m
// More operator implementations...
}
val Number.km get() = Length(this.toDouble() * 1e3)
val Number.m get() = Length(this.toDouble())
val Number.cm get() = Length(this.toDouble() * 1e-2)
val Number.mm get() = Length(this.toDouble() * 1e-3)
// More extension functions for other units...
因此,数据类现在如下所示:
data class SampleClassThatContainsALength(val length: Length)
示例对象序列化为 .SampleClassThatContainsALength(0.025.m)
{"length":{"m":0.025}}
我现在有一个问题,我想使用新的序列化,因为它清楚地说明了单位,但我仍然有一些旧的JSON文档(类似于开头的序列化JSON),我想将其隐式转换为新的实现。
所以,总而言之,我想要两者,并反序列化为 .{"length":0.025}
{"length":{"m":0.025}}
SampleClassThatContainsALength(0.025.m)
现在,反序列化按预期工作,但反序列化失败{"length":{"m":0.025}}
{"length":0.025}
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `de.uni_freiburg.inatech.streem.image_converter.common.math.units.Length` (although at least one Creator exists): no double/Double-argument constructor/factory method to deserialize from Number value (0.025)
at [Source: (String)"{"length":0.025}"; line: 1, column: 11] (through reference chain: de.uni_freiburg.inatech.streem.image_converter.data.json.SampleClassThatContainsALength["length"])
这是我到目前为止尝试过的:
- 我在类中创建了一个,并添加了以下工厂方法:
companion object
Length
companion object {
@JsonCreator
@JvmStatic
fun createFromNumber(number: Number): Length = Length(number.toDouble())
}
这会导致 的反序列化起作用,但现在新格式的反序列化不再起作用:{"length":0.025}
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.Number` from Object value (token `JsonToken.FIELD_NAME`)
at [Source: (byte[])[19 bytes]; byte offset: #7]
- 除了配套对象中的工厂方法外,我还尝试将 的主构造函数标记为如下所示:
Length
@JsonCreator
data class Length @JsonCreator constructor(val m: Double) : Comparable<Length> {
但结果与尝试 1 完全相同。
- 我没有使用工厂方法,而是添加了一个接受(而不是):
Number
Double
data class Length(val m: Double) : Comparable<Length> {
constructor(m: Number) : this(m.toDouble())
虽然新格式现在正在工作,但旧格式仍然被打破。
答:
哇,这比我想象的要快,我有点惭愧,我在发布之前没有找到这个。尽管如此,对于遇到相同问题的人来说,这是解决方案:
- 将构造函数保留原样:
// No annotation and no secondary constructor necessary
data class Length(val m: Double) : Comparable<Length> {
// ...
- 为这两种情况创建两个工厂方法:
companion object {
// Handles the case {"length":{"m":0.025}}
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
@JvmStatic
fun createFromObject(m: Double): Length = Length(m)
// Handles the case {"length":0.025}
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
@JvmStatic
fun createFromNumber(m: String): Length = Length(m.toDouble())
}
请注意,现在需要 a 而不是 or ,这似乎很重要。createFromNumber
String
Number
Double
以下是激励我这样做的原因:
评论