Kotlin Json 序列化

騷銘科技發表於2022-11-24

Kotlin可以直接呼叫 Java(也就可以直接使用 Jackson, Gson 等json庫),不過我看到有 official 的序列化庫,於是研究了一下。

重點看了下多型的序列化,確實非常簡潔,先上程式碼感受一下:

@Serializable
sealed interface Reply

@Serializable
@SerialName("text")
class TextReply(val content: String): Reply

@Serializable
@SerialName("card")
class CardReply(val content: String, val cardId: Long = 1L): Reply

fun main() {
    val reply: Reply = CardReply("A card", 2L)
    // 序列化
    val jsonString = Json.encodeToString(reply)
    println(jsonString)

    // 反序列化,隱式
    val decodedReply: Reply = Json.decodeFromString(jsonString)
    println(decodedReply)
    // 顯式
    println( Json.decodeFromString<Reply>(jsonString) )
}

Output:

{"type":"card","content":"A card","cardId":2}
CardReply@2d6a9952
CardReply@22a71081

CardReply & TextReply 實現了 Reply,可以看到序列化結果自動加上了型別標識"type": "card"


再來看看泛型的序列化,同樣十分簡潔:

fun main() {
    // 列表序列化
    val jsonList = Json.encodeToString(
        listOf(CardReply("A card"), TextReply("A text"))
    )
    println(jsonList)
    // 列表反序列化
    val replies: List<Reply> = Json.decodeFromString(jsonList)
    println(replies)
}

Output:

[{"type":"card","content":"A card"},{"type":"text","content":"A text"}]
[CardReply@25618e91, TextReply@7a92922]

可以看到,泛型沒有丟失,good。

用過 Jackson 的同學應該知道這個神坑,給個單元測試感受一下:

public class ReplyTest {

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            defaultImpl = CardReply.class
    )
    @JsonSubTypes({
            @JsonSubTypes.Type(value = CardReply.class, name = "card")
    })
    interface Reply { }

    static class CardReply implements Reply { }

    @Test
    void fuck() throws JsonProcessingException {
        System.out.println( JsonUtil.toStr(new CardReply()) );

        String json = JsonUtil.toStr( Collections.singletonList(new CardReply()) );
        System.out.println(json);
    }
}

Output:

{ "@type" : "card" }
[ { } ]

可以看到,List<Reply>序列化結果丟失了泛型,令人沮喪。

至此,Kotlin 給人感覺還不錯,程式碼簡潔,對多型支援良好。


詳情請參考官方庫:https://github.com/Kotlin/kot...

相關文章