kotlinx序列化器解码蛇的情况下 Camel 的情况下没有序列名称

7kqas0il  于 2023-11-21  发布在  Kotlin
关注(0)|答案(1)|浏览(142)

提问

嗨,我是kotlinx序列化的新手。json是snake case,kotlin数据类使用camel case。有没有一种方法可以通过使用自定义serilizer和parallelizer来解析snake case?
我知道怎么做...

val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }

val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""")
assertThat(project.projectName).isEqualTo("kotlinx.coroutines")
assertThat(project.projectOwner).isEqualTo("Kotlin")

字符串
但是我想重写编译器并把这个逻辑放在伴随对象里面。这可能吗?

我的代码

import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encodeToString
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy

@kotlinx.serialization.ExperimentalSerializationApi
private val json = Json { namingStrategy = JsonNamingStrategy.SnakeCase }

@Serializable(PaymentInfo.Companion::class)
data class PaymentInfo(
    val paymentNo: String,
    val paymentDate: String,
    val paymentService: String,
    
    ) {
    companion object: KSerializer<PaymentInfo> {
        // what descriptor should I use?
        override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("PaymentInfo", PrimitiveKind.)

        override fun serialize(encoder: Encoder, value: PaymentInfo) {
            // what to do ?
        }
        override fun deserialize(decoder: Decoder): PaymentInfo {
            // what to do ?
        }
    }
}

已编辑

我想避免添加@SerialName到每个成员!

ugmeyewa

ugmeyewa1#

@SerialName

首先,我想提一下,我不明白你为什么要避免@SerialName。你提到:
恐怕太费力了。
但是正如你将在本答案的下一节中看到的,自定义序列化器是相当多的代码。同样的事情可以通过以下方式完成:

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class PaymentInfo(
    @SerialName("payment_no") val paymentNo: String,
    @SerialName("payment_date") val paymentDate: String,
    @SerialName("payment_service") val paymentService: String
)

字符串
对于像更改属性的序列名称这样简单的操作,我强烈建议您使用@SerialName,而不是编写完全自定义的序列化程序。

自定义序列化器

如果你真的想避免@SerialName,那么你可以通过自定义序列化器自定义序列化的名称。如果你有:

import kotlinx.serialization.Serializable

@Serializable(PaymentInfoSerializer::class)
data class PaymentInfo(
    val paymentNo: String,
    val paymentDate: String,
    val paymentService: String
)


您需要以下自定义名称:
| 类属性|系列产品名称|
| --|--|
| paymentNo个|payment_no个|
| paymentDate个|payment_date个|
| paymentService个|payment_service个|
那么自定义的PaymentInfoSerializer可能看起来像这样:

import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.*

// You can move this to the companion object of PaymentInfo if
// you really want to.
object PaymentInfoSerializer : KSerializer<PaymentInfo> {

    override val descriptor = buildClassSerialDescriptor("PaymentInfo") {
        element<String>("payment_no")
        element<String>("payment_date")
        element<String>("payment_service")
    }

    override fun serialize(encoder: Encoder, value: PaymentInfo) {
        encoder.encodeStructure(descriptor) {
            encodeStringElement(descriptor, 0, value.paymentNo)
            encodeStringElement(descriptor, 1, value.paymentDate)
            encodeStringElement(descriptor, 2, value.paymentService)
        }
    }

    override fun deserialize(decoder: Decoder): PaymentInfo {
        return decoder.decodeStructure(descriptor) {
            var paymentNo = ""
            var paymentDate = ""
            var paymentService = ""
            while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> paymentNo = decoder.decodeString()
                    1 -> paymentDate = decoder.decodeString()
                    2 -> paymentService = decoder.decodeString()
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unknown index: $index")
                }
            }
            PaymentInfo(paymentNo, paymentDate, paymentService)
        }
    }
}


有关详细信息,请参阅 * Kotlin序列化指南 * 的序列化器一章。
下面是上述序列化器的用途:

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

fun main() {
    val original = PaymentInfo("42", "2023-11-12", "foo")
    val encoded = Json.encodeToString(original)
    val decoded = Json.decodeFromString<PaymentInfo>(encoded)
    println(original)
    println(encoded)
    println(decoded)
}


它给出以下输出:

PaymentInfo(paymentNo=42, paymentDate=2023-11-12, paymentService=foo)
{"payment_no":"42","payment_date":"2023-11-12","payment_service":"foo"}
PaymentInfo(paymentNo=42, paymentDate=2023-11-12, paymentService=foo)

相关问题