我有一个简单的数据类,代表一个城市
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
}
}
如果你真的要保持兼容性,你可以写自己的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)
. 即使你已经在别名之上定义了命名的属性获取器,你也不需要去定义一个自定义的序列化器和反序列化器,这总是很好的。