如何用Jackson将JSON对象反序列化到Kotlin Pair中?

问题描述 投票:2回答:1

我有一个简单的数据类,代表一个城市

data class City(
    val name: String,
    val centroid: Coordinates
)

出于外部兼容性的原因。Coordinates 类型被定义为一个 typealias

typealias Coordinates = Pair<Double, Double>

val Coordinates.lat
    get() = first

val Coordinates.lon
    get() = second

我如何配置Jackson,使其能够将以下JSON反序列化为一个实例?City?

{
  "name": "Praha",
  "centroid": {
    "lat": 50.2141,
    "lon": 14.42342
  }
}
kotlin jackson
1个回答
2
投票

如果你真的要保持兼容性,你可以写自己的Serializer & Deserializer,例如。

object CoordinatesConversions {

    const val LATITUDE_FIELD_NAME = "lat"
    const val LONGITUDE_FIELD_NAME = "lon"

    object Serializer : JsonSerializer<Coordinates>() {
        override fun serialize(value: Coordinates, gen: JsonGenerator, serializers: SerializerProvider) {
            with(gen) {
                writeStartObject()
                writeNumberField(LATITUDE_FIELD_NAME, value.first)
                writeNumberField(LONGITUDE_FIELD_NAME, value.second)
                writeEndObject()
            }
        }
    }

    object Deserializer : JsonDeserializer<Coordinates>() {
        override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Coordinates {
            val node = p.readValueAsTree<JsonNode>()

            val lat = node.get(LATITUDE_FIELD_NAME).asDouble()
            val lon = node.get(LONGITUDE_FIELD_NAME).asDouble()

            return Coordinates(lat, lon)
        }
    }
}

同时指定哪些序列化器应该用于你的城市模型。

data class City(
    val name: String,

    @JsonSerialize(using = CoordinatesConversions.Serializer::class)
    @JsonDeserialize(using = CoordinatesConversions.Deserializer::class)
    val center: Coordinates
)

一个完全可行的例子。

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue

private typealias Coordinates = Pair<Double, Double>

object CoordinatesConversions {

    const val LATITUDE_FIELD_NAME = "lat"
    const val LONGITUDE_FIELD_NAME = "lon"

    object Serializer : JsonSerializer<Coordinates>() {
        override fun serialize(value: Coordinates, gen: JsonGenerator, serializers: SerializerProvider) {
            with(gen) {
                writeStartObject()
                writeNumberField(LATITUDE_FIELD_NAME, value.first)
                writeNumberField(LONGITUDE_FIELD_NAME, value.second)
                writeEndObject()
            }
        }
    }

    object Deserializer : JsonDeserializer<Coordinates>() {
        override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Coordinates {
            val node = p.readValueAsTree<JsonNode>()

            val lat = node.get(LATITUDE_FIELD_NAME).asDouble()
            val lon = node.get(LONGITUDE_FIELD_NAME).asDouble()

            return Coordinates(lat, lon)
        }
    }
}

object StackOverflow {

    private val objectMapper = jacksonObjectMapper().apply {
        configure(SerializationFeature.INDENT_OUTPUT, true)
    }

    data class City(
        val name: String,

        @JsonSerialize(using = CoordinatesConversions.Serializer::class)
        @JsonDeserialize(using = CoordinatesConversions.Deserializer::class)
        val center: Coordinates
    )

    @JvmStatic
    fun main(args: Array<String>) {

        val city = City("Cair Paravel", 13.37 to 42.0)

        val jsonOutput = objectMapper.writeValueAsString(city)
        println(jsonOutput)

        val deserializedCity = objectMapper.readValue<City>(jsonOutput)
        println(deserializedCity)
    }
}

我还是认为,最好不要再使用Pairs来表示这样一个具体的数据结构,我想这对你将来会有好处。

例如,在表示一个地理点的时候,并不总是很清楚那是用的哪个标准,有些厂商用 (lat, lng),其他人使用 (lng, lat). 即使你已经在别名之上定义了命名的属性获取器,你也不需要去定义一个自定义的序列化器和反序列化器,这总是很好的。

© www.soinside.com 2019 - 2024. All rights reserved.