使用自定义解码器在 Kotlin 中反序列化 JSON 可为空的字段时遇到问题
Having trouble with deserialising JSON nullable fields in Kotlin with custom decoder
我在使用 Kotlin 序列化解码此 JSON 时遇到困难。当数据字段不为空时效果很好。但是,当数据字段为 null 且存在错误字段时,我收到此运行时异常:
kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 7: Expected start of the object '{', but had ':' instead
JSON input: {"data":null,"errors":[{"path":null,"locations":[{"line":3,"column":5,"sourceName":null}],"message":"Validation error of type FieldUndefined: Field 'mee' in type 'Query' is undefined @ 'mee'"}]})
JSON 印刷精美:
{
"data": null,
"errors": [{
"path": null,
"locations": [{
"line": 3,
"column": 5,
"sourceName": null
}],
"message": "Validation error of type FieldUndefined: Field 'mee' in type 'Query' is undefined @ 'mee'"
}]
}
主要是从How to serialize a generic class with kontlinx.serialization?偷来的代码:
class ApiResponse<T>(
@SerialName("data")
val data: T? = null,
@SerialName("errors")
val errors: List<ErrorResponse>? = null
)
@Serializable
class ErrorResponse(
val path: String? = null,
val locations: List<Location>? = null,
val errorType: String? = null,
val message: String? = null
)
@Serializable
data class Location(
val line: Int? = 0,
val column: Int? = 0,
val sourceName: String? = null
)
@ExperimentalSerializationApi
class ApiResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<ApiResponse<T>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ApiResponseDataSerializer") {
val dataDescriptor = dataSerializer.descriptor
element("data", dataDescriptor)
element("errors", ErrorResponse.serializer().descriptor)
}
override fun deserialize(decoder: Decoder): ApiResponse<T> =
decoder.decodeStructure(descriptor) {
var data: T? = null
var errors: List<ErrorResponse>? = null
val listSerializer = ListSerializer(ErrorResponse.serializer())
loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) {
0 -> data = decodeSerializableElement(descriptor, i, dataSerializer, null)
1 -> errors = decodeSerializableElement(descriptor, i, ListSerializer(ErrorResponse.serializer()), null)
CompositeDecoder.DECODE_DONE -> break
else -> throw SerializationException("Unknown index $i")
}
}
ApiResponse(data, errors)
}
override fun serialize(encoder: Encoder, value: ApiResponse<T>) {
encoder.encodeStructure(descriptor) {
val listSerializer = ListSerializer(ErrorResponse.serializer())
encodeNullableSerializableElement(descriptor, 0, dataSerializer, value.data)
value.errors?.let {
encodeNullableSerializableElement(descriptor, 1, listSerializer, it)
}
}
}
}
我尝试使用 decodeNullableSerializableElement,但出现编译错误。我找不到解决这个问题的方法。
Type mismatch: inferred type is KSerializer<T> but DeserializationStrategy<TypeVariable(T)?> was expected
任何帮助将不胜感激,我是 Android 和 Kotlin 的新手。
睡个好觉后回来总是值得的。不知道为什么我昨天在使用 decodeNullableSerializableElement 时遇到了这么多麻烦,但今天我玩了一下并让它工作了。
进行了 3 次更改
- 在 class 参数中使 T 可选
- 向两个序列化器添加了 .nullable(昨天无法正常工作)
- 将 decodeSerializableElement 更改为 decodeNullableSerializableElement
相关变更如下:
@ExperimentalSerializationApi
class ApiResponseSerializer<T>(private val dataSerializer: KSerializer<T?>) : KSerializer<ApiResponse<T>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ApiResponseDataSerializer") {
val dataDescriptor = dataSerializer.descriptor
element("data", dataDescriptor.nullable)
element("errors", ErrorResponse.serializer().descriptor.nullable)
}
override fun deserialize(decoder: Decoder): ApiResponse<T> =
decoder.decodeStructure(descriptor) {
var data: T? = null
var errors: List<ErrorResponse>? = null
val listSerializer = ListSerializer(ErrorResponse.serializer()).nullable
loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) {
0 -> data = decodeNullableSerializableElement(descriptor, i, dataSerializer, null)
1 -> errors = decodeNullableSerializableElement(descriptor, i, listSerializer, null)
CompositeDecoder.DECODE_DONE -> break
else -> throw SerializationException("Unknown index $i")
}
}
ApiResponse(data, errors)
}
override fun serialize(encoder: Encoder, value: ApiResponse<T>) {
encoder.encodeStructure(descriptor) {
val listSerializer = ListSerializer(ErrorResponse.serializer())
encodeNullableSerializableElement(descriptor, 0, dataSerializer, value.data)
value.errors?.let {
encodeNullableSerializableElement(descriptor, 1, listSerializer, it)
}
}
}
}
我在使用 Kotlin 序列化解码此 JSON 时遇到困难。当数据字段不为空时效果很好。但是,当数据字段为 null 且存在错误字段时,我收到此运行时异常:
kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 7: Expected start of the object '{', but had ':' instead
JSON input: {"data":null,"errors":[{"path":null,"locations":[{"line":3,"column":5,"sourceName":null}],"message":"Validation error of type FieldUndefined: Field 'mee' in type 'Query' is undefined @ 'mee'"}]})
JSON 印刷精美:
{
"data": null,
"errors": [{
"path": null,
"locations": [{
"line": 3,
"column": 5,
"sourceName": null
}],
"message": "Validation error of type FieldUndefined: Field 'mee' in type 'Query' is undefined @ 'mee'"
}]
}
主要是从How to serialize a generic class with kontlinx.serialization?偷来的代码:
class ApiResponse<T>(
@SerialName("data")
val data: T? = null,
@SerialName("errors")
val errors: List<ErrorResponse>? = null
)
@Serializable
class ErrorResponse(
val path: String? = null,
val locations: List<Location>? = null,
val errorType: String? = null,
val message: String? = null
)
@Serializable
data class Location(
val line: Int? = 0,
val column: Int? = 0,
val sourceName: String? = null
)
@ExperimentalSerializationApi
class ApiResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<ApiResponse<T>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ApiResponseDataSerializer") {
val dataDescriptor = dataSerializer.descriptor
element("data", dataDescriptor)
element("errors", ErrorResponse.serializer().descriptor)
}
override fun deserialize(decoder: Decoder): ApiResponse<T> =
decoder.decodeStructure(descriptor) {
var data: T? = null
var errors: List<ErrorResponse>? = null
val listSerializer = ListSerializer(ErrorResponse.serializer())
loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) {
0 -> data = decodeSerializableElement(descriptor, i, dataSerializer, null)
1 -> errors = decodeSerializableElement(descriptor, i, ListSerializer(ErrorResponse.serializer()), null)
CompositeDecoder.DECODE_DONE -> break
else -> throw SerializationException("Unknown index $i")
}
}
ApiResponse(data, errors)
}
override fun serialize(encoder: Encoder, value: ApiResponse<T>) {
encoder.encodeStructure(descriptor) {
val listSerializer = ListSerializer(ErrorResponse.serializer())
encodeNullableSerializableElement(descriptor, 0, dataSerializer, value.data)
value.errors?.let {
encodeNullableSerializableElement(descriptor, 1, listSerializer, it)
}
}
}
}
我尝试使用 decodeNullableSerializableElement,但出现编译错误。我找不到解决这个问题的方法。
Type mismatch: inferred type is KSerializer<T> but DeserializationStrategy<TypeVariable(T)?> was expected
任何帮助将不胜感激,我是 Android 和 Kotlin 的新手。
睡个好觉后回来总是值得的。不知道为什么我昨天在使用 decodeNullableSerializableElement 时遇到了这么多麻烦,但今天我玩了一下并让它工作了。
进行了 3 次更改
- 在 class 参数中使 T 可选
- 向两个序列化器添加了 .nullable(昨天无法正常工作)
- 将 decodeSerializableElement 更改为 decodeNullableSerializableElement
相关变更如下:
@ExperimentalSerializationApi
class ApiResponseSerializer<T>(private val dataSerializer: KSerializer<T?>) : KSerializer<ApiResponse<T>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ApiResponseDataSerializer") {
val dataDescriptor = dataSerializer.descriptor
element("data", dataDescriptor.nullable)
element("errors", ErrorResponse.serializer().descriptor.nullable)
}
override fun deserialize(decoder: Decoder): ApiResponse<T> =
decoder.decodeStructure(descriptor) {
var data: T? = null
var errors: List<ErrorResponse>? = null
val listSerializer = ListSerializer(ErrorResponse.serializer()).nullable
loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) {
0 -> data = decodeNullableSerializableElement(descriptor, i, dataSerializer, null)
1 -> errors = decodeNullableSerializableElement(descriptor, i, listSerializer, null)
CompositeDecoder.DECODE_DONE -> break
else -> throw SerializationException("Unknown index $i")
}
}
ApiResponse(data, errors)
}
override fun serialize(encoder: Encoder, value: ApiResponse<T>) {
encoder.encodeStructure(descriptor) {
val listSerializer = ListSerializer(ErrorResponse.serializer())
encodeNullableSerializableElement(descriptor, 0, dataSerializer, value.data)
value.errors?.let {
encodeNullableSerializableElement(descriptor, 1, listSerializer, it)
}
}
}
}