【Kotlin】kotlinx.serialization Vs Gson

1. 簡介

2. 設計理念

3. 使用方式

1. 類型擦除(Type Erasure)問題
 
2. kotlinx.serialization 的型別安全

kotlinx.serialization 是 Kotlin 原生的序列化庫,它能夠利用 Kotlin 的反射和內聯函數特性在編譯時和運行時保留類型信息,避免了類型擦除問題。這意味著你可以直接在編譯期獲取類型信息,從而在運行時保留完整的類型安全性。

範例:

@Serializable
data class People<T>(var value: T)
@Serializable
data class Man(val name: String)
  
   val people = People(Man("John"))
   val jsonString = Json.encodeToString(people)
   println(jsonString) // {"value":{"name":"John"}}
   val people2 = Json.decodeFromString<People<Man>>(jsonString)
   println(people2) // People(value=Man(name=John))

在這個例子中,kotlinx.serialization 使用 decodeFromString<User>(jsonString) 方法時,直接保留了 User 類型的信息,而無需額外提供類型提示。這是因為 Kotlin 的 inline 函數和 reified 泛型可以在運行時保留類型信息。

3. 優勢和好處

 

4. 特性比較

在 kotlinx.serialization 中,有多種設定可以控制 JSON 序列化和反序列化的行為,這些設定可以通過 Json 配置進行調整。以下是對這些設定的詳細補充說明,以及如何混合使用它們:

1. prettyPrint

2. isLenient

3. ignoreUnknownKeys

4. @JsonNames

5. encodeDefaults

6. explicitNulls

7. allowStructuredMapKeys

8. allowSpecialFloatingPointValues

如何混合使用這些配置

你可以將這些配置項混合使用來滿足特定的序列化和反序列化需求。

例如,假設你有一個情況需要反序列化非標準的 JSON 資料,並且需要忽略未知的鍵保留結構化的 Map 鍵:

val json = Json {
    prettyPrint = true
    isLenient = true
    ignoreUnknownKeys = true
    encodeDefaults = false
    explicitNulls = false
    coerceInputValues = true
    allowStructuredMapKeys = true
    allowSpecialFloatingPointValues = true
}

val data = json.decodeFromString<User>("""{"name":"Alice", "unknownField":"value"}""")
println(data)
// User(name=Alice, age=30)
val gson = GsonBuilder()
    .setPrettyPrinting()
    .setLenient()
    .serializeNulls()  // Include nulls
    .registerTypeAdapter(User::class.java, JsonDeserializer { json, _, _ ->
        val jsonObject = json.asJsonObject
        val name = if (jsonObject.has("name")) jsonObject["name"].asString else "DefaultName"
        val age = if (jsonObject.has("age")) jsonObject["age"].asInt else 30 // Default value
        User(name, age)
    })
    .create()

val jsonString = """{"name":"Alice","unknownField":"value"}"""
val user: User = gson.fromJson(jsonString, User::class.java)
println(user) //User(name=Alice, age=30)
println(gson.toJson(User("Alice")))
  /*
  {
    "name": "Alice",
    "age": 30
  }
  */

在這個例子中,我們結合了多種配置來實現靈活且容錯的 JSON 解析和輸出,確保代碼能夠應對多種 JSON 格式和數據問題。這樣的設置可以讓你的應用更加健壯和靈活。

json default value:

/**
 * Configuration of the current [Json] instance available through [Json.configuration]
 * and configured with [JsonBuilder] constructor.
 *
 * Can be used for debug purposes and for custom Json-specific serializers
 * via [JsonEncoder] and [JsonDecoder].
 *
 * Standalone configuration object is meaningless and can nor be used outside the
 * [Json], neither new [Json] instance can be created from it.
 *
 * Detailed description of each property is available in [JsonBuilder] class.
 */
public class JsonConfiguration @OptIn(ExperimentalSerializationApi::class) internal constructor(
    public val encodeDefaults: Boolean = false,
    public val ignoreUnknownKeys: Boolean = false,
    public val isLenient: Boolean = false,
    public val allowStructuredMapKeys: Boolean = false,
    public val prettyPrint: Boolean = false,
    public val explicitNulls: Boolean = true,
    @ExperimentalSerializationApi
    public val prettyPrintIndent: String = "    ",
    public val coerceInputValues: Boolean = false,
    public val useArrayPolymorphism: Boolean = false,
    public val classDiscriminator: String = "type",
    public val allowSpecialFloatingPointValues: Boolean = false,
    public val useAlternativeNames: Boolean = true,
    @ExperimentalSerializationApi
    public val namingStrategy: JsonNamingStrategy? = null,
    @ExperimentalSerializationApi
    public val decodeEnumsCaseInsensitive: Boolean = false,
    @ExperimentalSerializationApi
    public val allowTrailingComma: Boolean = false,
    @ExperimentalSerializationApi
    public val allowComments: Boolean = false,
    @ExperimentalSerializationApi
    @set:Deprecated(
        "JsonConfiguration is not meant to be mutable, and will be made read-only in a future release. " +
            "The `Json(from = ...) {}` copy builder should be used instead.",
        level = DeprecationLevel.ERROR
    )
    public var classDiscriminatorMode: ClassDiscriminatorMode = ClassDiscriminatorMode.POLYMORPHIC,
) {

    /** @suppress Dokka **/
    @OptIn(ExperimentalSerializationApi::class)
    override fun toString(): String {
        return "JsonConfiguration(encodeDefaults=$encodeDefaults, ignoreUnknownKeys=$ignoreUnknownKeys, isLenient=$isLenient, " +
                "allowStructuredMapKeys=$allowStructuredMapKeys, prettyPrint=$prettyPrint, explicitNulls=$explicitNulls, " +
                "prettyPrintIndent='$prettyPrintIndent', coerceInputValues=$coerceInputValues, useArrayPolymorphism=$useArrayPolymorphism, " +
                "classDiscriminator='$classDiscriminator', allowSpecialFloatingPointValues=$allowSpecialFloatingPointValues, " +
                "useAlternativeNames=$useAlternativeNames, namingStrategy=$namingStrategy, decodeEnumsCaseInsensitive=$decodeEnumsCaseInsensitive, " +
                "allowTrailingComma=$allowTrailingComma, allowComments=$allowComments, classDiscriminatorMode=$classDiscriminatorMode)"
    }
}

 

 


5. 擴展性和客製化

在 JSON 序列化和反序列化過程中,kotlinx.serialization 和 Gson 都提供了強大的擴展性和客製化能力。這些能力允許開發者根據需要定義自定義的序列化和反序列化邏輯,以處理特殊格式的數據或滿足特定需求。

kotlinx.serialization 的擴展性和客製化

kotlinx.serialization 提供了一個簡潔的方式來自定義序列化和反序列化邏輯,通過實作 KSerializer 介面,你可以完全控制對象的序列化和反序列化過程。

如何實作自定義序列化器
  1. 定義資料類

    @Serializable
    data class Event(val name: String,
                     @Serializable(with = DateSerializer::class) val date: Date)
    
    

    在這個例子中,Event 類的 date 屬性使用了自定義序列化器 DateSerializer 來處理 Date 類型。

  2. 實作 KSerializer 介面

    object DateSerializer : KSerializer<Date> {
        override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
    
        override fun serialize(encoder: Encoder, value: Date) {
            // 自定義的序列化邏輯,將 Date 轉換為 String
            val dateFormat = SimpleDateFormat("yyyy-MM-dd")
            val dateString = dateFormat.format(value)
            encoder.encodeString(dateString)
        }
    
        override fun deserialize(decoder: Decoder): Date {
            // 自定義的反序列化邏輯,將 String 轉換為 Date
            val dateFormat = SimpleDateFormat("yyyy-MM-dd")
            val dateString = decoder.decodeString()
            return dateFormat.parse(dateString)
        }
    }
    

    在這個例子中:

    • serialize 方法將 Date 對象序列化為 String 格式。
    • deserialize 方法將 String 反序列化為 Date 對象。
  3. 使用自定義序列化器

    val json = Json { }
    val event = Event("Conference", Date())
    val jsonString = json.encodeToString(event)
    println(jsonString) // {"name":"Conference","date":"2024-08-29"}
    
    val deserializedEvent = json.decodeFromString<Event>(jsonString)
    println(deserializedEvent)
    // Event(name=Conference, date=Thu Aug 29 00:00:00 CST 2024)

    自定義序列化器 DateSerializer 會自動在 encodeToString 和 decodeFromString 操作中被使用,處理 Event 類中的 date 屬性。

Gson 的擴展性和客製化

在 Gson 中,客製化序列化和反序列化是通過實作 JsonSerializer 和 JsonDeserializer 介面來實現的。這提供了類似的靈活性,可以完全控制對象的序列化和反序列化過程。

如何實作自定義序列化器和反序列化器
  1. 定義資料類

    data class Event(val name: String, val date: Date)
    

    與 kotlinx.serialization 不同,Gson 並不需要在資料類上添加任何特別的註解。

  2. 實作 JsonSerializer 和 JsonDeserializer 介面

    val dateSerializer = JsonSerializer<Date> { src, _, _ ->
        // 自定義的序列化邏輯,將 Date 轉換為 String
        val dateFormat = SimpleDateFormat("yyyy-MM-dd")
        JsonPrimitive(dateFormat.format(src))
    }
    
    val dateDeserializer = JsonDeserializer<Date> { json, _, _ ->
        // 自定義的反序列化邏輯,將 String 轉換為 Date
        val dateFormat = SimpleDateFormat("yyyy-MM-dd")
        dateFormat.parse(json.asString)
    }
    

    在這個例子中:

    • dateSerializer 是一個 JsonSerializer<Date>,負責將 Date 序列化為 JSON 字符串。
    • dateDeserializer 是一個 JsonDeserializer<Date>,負責將 JSON 字符串反序列化為 Date 對象。
  3. 使用自定義序列化器和反序列化器

    val gson = GsonBuilder()
        .registerTypeAdapter(Date::class.java, dateSerializer)
        .registerTypeAdapter(Date::class.java, dateDeserializer)
        .create()
    
    val event = Event("Conference", Date())
    val jsonString = gson.toJson(event)
    println(jsonString)
      // {"name":"Conference","date":"2024-08-29"}
    
    val deserializedEvent = gson.fromJson(jsonString, Event::class.java)
    println(deserializedEvent)
     // Event(name=Conference, date=Thu Aug 29 00:00:00 CST 2024)

    在這個例子中,registerTypeAdapter 方法用來註冊自定義的序列化器和反序列化器。Gson 會在序列化和反序列化 Date 類型時使用這些自定義的邏輯。

總結

這兩者都提供了足夠的靈活性來滿足大多數序列化和反序列化需求,不過 kotlinx.serialization 更加貼合 Kotlin 語言的特性和風格,而 Gson 則更加通用且適用於 Java 開發環境。

6. 依賴管理與體積

7. 社群與支援

8. 總結與建議

 


修訂版本 #1
由 treeman 建立於 2 E 2024 09:54:14
由 treeman 更新於 2 E 2024 09:54:26