0

我正在努力理解如何{}用实验kotlinx.serialization库解析一个空对象。事实上,当 API 响应可能是其中之一时,就会出现复杂情况:

{
  "id": "ABC1",
  "status": "A_STATUS"
}

或者

{}

我用作序列化程序的数据结构是;

data class Thing(val id: String = "", val status: String = "")

这在 API 客户端库中注释@kotlinx.serialization.Serializable并用于在原始 API 响应和数据模型之间编组。默认值告诉序列化库该字段是可选的,并替换了@OptionalKotlin 1.3.30 之前的方法。

最后,kotlinx.serialization.json.Json我使用的解析器使用nonstrict模板应用了配置。

如何定义一个可以同时解析空对象和预期数据类型的序列化程序kotlinx.serialization?我需要自己编写KSerialiser还是缺少配置。理想情况下,空对象应该被忽略/解析为null

使用我的Thing数据类解析空对象时出现的错误是;

Field 'id' is required, but it was missing
4

2 回答 2

1

所以这归结为kotlinCompilerClasspath拥有不同版本的 kotlin(1.3.21,而不是 1.3.31)。

有趣的是,这是由于我在配置我的 gradle 插件项目时遵循的建议,即为插件指定版本kotlin-dsl

明确依赖我需要的版本修复了kotlinx.serialisation行为(主线代码没有更改)

于 2019-06-16T17:17:13.267 回答
-1

是的,理想情况下,null而不是{}更方便解析,但有时您只需要使用后端发送给您的内容

我想到了2个解决方案。

  1. 更简单,特定于您使用地图的情况:
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

class ThingMapSerializerTest {

    @Test
    fun `should deserialize to non empty map`() {
        val thingMap: Map<String, String> =
            Json.decodeFromString("""{"id":"ABC1","status":"A_STATUS"}""")

        assertTrue(thingMap.isNotEmpty())
        assertEquals("ABC1", thingMap["id"])
        assertEquals("A_STATUS", thingMap["status"])
    }

    @Test
    fun `should deserialize to empty map`() {
        val thingMap: Map<String, String> = Json.decodeFromString("{}")

        assertTrue(thingMap.isEmpty())
    }
}
  1. 更复杂但更通用,适用于值类型的任何组合。我推荐具有显式空值的密封类,而不是具有空默认值的数据类:
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.serialDescriptor
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
import org.junit.Test

class ThingSerializerTest {

    @Test
    fun `should deserialize to thing`() {
        val thing: OptionalThing =
            Json.decodeFromString(
                OptionalThing.ThingSerializer,
                """{"id":"ABC1","status":"A_STATUS"}"""
            )

        assertEquals(OptionalThing.Thing(id = "ABC1", status = "A_STATUS"), thing)
    }

    @Test
    fun `should deserialize to empty`() {
        val thing: OptionalThing =
            Json.decodeFromString(OptionalThing.ThingSerializer, "{}")

        assertEquals(OptionalThing.Empty, thing)
    }

    sealed class OptionalThing {
        data class Thing(val id: String = "", val status: String = "") : OptionalThing()
        object Empty : OptionalThing()

        object ThingSerializer : KSerializer<OptionalThing> {
            override val descriptor: SerialDescriptor =
                buildClassSerialDescriptor("your.app.package.OptionalThing") {
                    element("id", serialDescriptor<String>(), isOptional = true)
                    element("status", serialDescriptor<String>(), isOptional = true)
                }

            override fun deserialize(decoder: Decoder): OptionalThing {
                decoder.decodeStructure(descriptor) {
                    var id: String? = null
                    var status: String? = null
                    loop@ while (true) {
                        when (val index = decodeElementIndex(descriptor)) {
                            CompositeDecoder.DECODE_DONE -> break@loop
                            0 -> id = decodeStringElement(descriptor, index = 0)
                            1 -> status = decodeStringElement(descriptor, index = 1)
                            else -> throw SerializationException("Unexpected index $index")
                        }
                    }
                    return if (id != null && status != null) Thing(id, status)
                    else Empty
                }
            }

            override fun serialize(encoder: Encoder, value: OptionalThing) {
                TODO("Not implemented, not needed")
            }
        }
    }

}

当 'Thing' 是 json 对象中的一个字段时:

  "thing":{"id":"ABC1","status":"A_STATUS"} // could be {} 

你可以这样注释属性:

@Serializable(with = OptionalThing.ThingSerializer::class)
val thing: OptionalThing

测试:

  • classpath "org.jetbrains.kotlin:kotlin-serialization:1.4.10"
  • implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
于 2021-01-26T22:24:23.760 回答