๊ธฐ์กด ์๋๋ก์ด๋์์๋ json ์ฒ๋ฆฌ์
gson ๊ณผ Moshi ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค.
๋๋ ์์ง๊น์ง gson์ด ํธํ๊ณ ๋ฐ๊ฟ์ผ ํ ํ์์ฑ์ ๋๋ผ์ง ๋ชป ํ์๋๋ฐ
์ต๊ทผ Compose๋ฅผ ๊ณต๋ถํ๋ฉด์
๊ตฌ๊ธ ํ๋ก์ ํธ์์ json ์ฒ๋ฆฌ๋ฅผ
Moshi -> kotlinx-serialization๋ก ๋ณ๊ฒฝํ๋ ์ถ์ธ๋ฅผ ๋ณด๊ณ
๊ด์ฌ์ด ์๊ฒจ ์ ๋ฆฌํด๋ณผ๋ ค๊ณ ํ๋ค.
โ
reflective mode ์ฝ๋ ์์ฑ์ ํตํด Gson ๋ฐ Moshi ๋ณด๋ค ๋น ๋ฅธ ๋ฐํ์ ์ฑ๋ฅ
โ
annotation ํ๋ก์ธ์๊ฐ ์๋ ์ปดํ์ผ๋ฌ ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ์ฌ Moshi๋ณด๋ค ๋น ๋ฅธ ๋น๋ ์๊ฐ
โ
์ฝํ๋ฆฐ์ ์ ํ ์์คํ
์ ๋ํ ์ง์ ํฅ์ ๋ฐ ๋ฌดํจ๊ฐ๊ณผ ๊ธฐ๋ณธ๊ฐ์ ๋ํ ์ง์
โ
๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชจ๋์ ์ฌ์ฉํ์ฌ ๋ค์ํ ์ธ์ฝ๋ฉ ํ์ ์ง์
๐๐ป ์๋๋ก์ด๋ codelab - retrofit2 with kotlinx.serialization
๐๐ป kotlinx.serialization
๐ ์์์ฝ๋ github
import kotlinx.serialization.Serializable
@Serializable
data class Person(
val name: String,
val age: Int = 31,
val sex: Char = 'M',
val hobby: String = "Game of League of Legends"
)
val json = Json {
encodeDefaults = true // encode ์ ๋ํดํธ ์ค์ ๊ฐ์ด ํฌํจ๋์ ์ธ์ฝ๋ฉ ๋จ
ignoreUnknownKeys = true // ์ฝํ๋ฆฐ ํด๋์ค๋ก ๋ณํ ์ json ์๋ ํ๋กํผํฐ๋ ๋ฌด์
}
public class JsonBuilder internal constructor(json: Json) {
/**
* Specifies whether default values of Kotlin properties should be encoded.
* `false` by default.
*/
public var encodeDefaults: Boolean = json.configuration.encodeDefaults
/**
* Specifies whether `null` values should be encoded for nullable properties and must be present in JSON object
* during decoding.
*
* When this flag is disabled properties with `null` values without default are not encoded;
* during decoding, the absence of a field value is treated as `null` for nullable properties without a default value.
*
* `true` by default.
*/
@ExperimentalSerializationApi
public var explicitNulls: Boolean = json.configuration.explicitNulls
/**
* Specifies whether encounters of unknown properties in the input JSON
* should be ignored instead of throwing [SerializationException].
* `false` by default.
*/
public var ignoreUnknownKeys: Boolean = json.configuration.ignoreUnknownKeys
/**
* Removes JSON specification restriction (RFC-4627) and makes parser
* more liberal to the malformed input. In lenient mode quoted boolean literals,
* and unquoted string literals are allowed.
*
* Its relaxations can be expanded in the future, so that lenient parser becomes even more
* permissive to invalid value in the input, replacing them with defaults.
*
* `false` by default.
*/
public var isLenient: Boolean = json.configuration.isLenient
/**
* Enables structured objects to be serialized as map keys by
* changing serialized form of the map from JSON object (key-value pairs) to flat array like `[k1, v1, k2, v2]`.
* `false` by default.
*/
public var allowStructuredMapKeys: Boolean = json.configuration.allowStructuredMapKeys
/**
* Specifies whether resulting JSON should be pretty-printed.
* `false` by default.
*/
public var prettyPrint: Boolean = json.configuration.prettyPrint
/**
* Specifies indent string to use with [prettyPrint] mode
* 4 spaces by default.
* Experimentality note: this API is experimental because
* it is not clear whether this option has compelling use-cases.
*/
@ExperimentalSerializationApi
public var prettyPrintIndent: String = json.configuration.prettyPrintIndent
/**
* Enables coercing incorrect JSON values to the default property value in the following cases:
* 1. JSON value is `null` but property type is non-nullable.
* 2. Property type is an enum type, but JSON value contains unknown enum member.
*
* `false` by default.
*/
public var coerceInputValues: Boolean = json.configuration.coerceInputValues
/**
* Switches polymorphic serialization to the default array format.
* This is an option for legacy JSON format and should not be generally used.
* `false` by default.
*/
public var useArrayPolymorphism: Boolean = json.configuration.useArrayPolymorphism
/**
* Name of the class descriptor property for polymorphic serialization.
* "type" by default.
*/
public var classDiscriminator: String = json.configuration.classDiscriminator
/**
* Removes JSON specification restriction on
* special floating-point values such as `NaN` and `Infinity` and enables their serialization and deserialization.
* When enabling it, please ensure that the receiving party will be able to encode and decode these special values.
* `false` by default.
*/
public var allowSpecialFloatingPointValues: Boolean = json.configuration.allowSpecialFloatingPointValues
/**
* Specifies whether Json instance makes use of [JsonNames] annotation.
*
* Disabling this flag when one does not use [JsonNames] at all may sometimes result in better performance,
* particularly when a large count of fields is skipped with [ignoreUnknownKeys].
* `true` by default.
*/
public var useAlternativeNames: Boolean = json.configuration.useAlternativeNames
/**
* Module with contextual and polymorphic serializers to be used in the resulting [Json] instance.
*/
public var serializersModule: SerializersModule = json.serializersModule
// kClass -> json (encode)
private fun encodePersonToJson(json: Json) {
val person = Person(
name = "david"
)
println("=======================Encode=======================")
println("person: $person")
println("Encode process")
println("json: ${json.encodeToString(person)}")
println("====================================================")
}
// ๊ฒฐ๊ณผ
=======================Encode=======================
person: Person(name=david, age=31, sex=M, hobby=Game of League of Legends)
Encode process
json: {"name":"david","age":31,"sex":"M","hobby":"Game of League of Legends"}
====================================================
// json -> kClass (decode)
private fun decodeJsonToPerson(json: Json) {
val jsonPerson = """
{
"name" : "david"
"age" : 32
"address" : "incheon" // Person์ ์๋ ํ๋กํผํฐ
}
"""
val person = json.decodeFromString<Person>(jsonPerson)
println("=======================Decode=======================")
println("json: $jsonPerson")
println("Decode process")
println("person: $person")
println("====================================================")
}
// ๊ฒฐ๊ณผ
=======================Decode=======================
json:
{
"name" : "david"
"age" : 32
"address" : "incheon"
}
Decode process
person: Person(name=david, age=32, sex=M, hobby=Game of League of Legends) ๐๐ป "address" ํ๋กํผํฐ ๋ฌด์
====================================================
package advanced
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
sealed class ViewType
@Serializable
@SerialName("vt001") // ๊ตฌ๋ถ ๋ช
์์๋ฅผ ์๋ ค์ค
data class VT001(
val title: String,
val data: List<String>
): ViewType()
@Serializable
@SerialName("vt002") // ๊ตฌ๋ถ ๋ช
์์๋ฅผ ์๋ ค์ค
data class VT002(
val title: String,
val keyword: String
): ViewType()
@Serializable
@SerialName("vt003") // ๊ตฌ๋ถ ๋ช
์์๋ฅผ ์๋ ค์ค
data class VT003(
val title: String,
val data: List<String>,
val views: Int
): ViewType()
val json = Json {
...
classDiscriminator = "viewType" // viewType์ผ๋ก ๊ตฌ๋ถ
}
expectJsonDecodeLog<VT003>(json) {
"""
{
"viewType" : "vt003",
"title" : "์ธ๊ธฐ ์ฝํ
์ธ ",
"data" : [
"์ฐ์ ์ฐธ๊ฒฌ", "์ฌ๋ฒ์ง ๋ง๋ด ์๋ค", "์ข
์ด์ ์ง"
],
"views" : 200
}
"""
}
// ๊ฒฐ๊ณผ
expect type[VT003]
JsonDecode type[VT003]
JsonDecode classInfo > VT003(title=์ธ๊ธฐ ์ฝํ
์ธ , data=[์ฐ์ ์ฐธ๊ฒฌ, ์ฌ๋ฒ์ง ๋ง๋ด ์๋ค, ์ข
์ด์ ์ง], views=200) ๐๐ป "VT003 ํด๋์ค๋ก ๋ณํ๋จ"
๊ณต์ ํ๋ฉฐ ๋๋ ์ ์ ๋ค์ํ ํ์
์ ์ง์ํ๋ฉด์
์ฝํ๋ฆฐ์ ์ฌ์ฉํ ๋ ๊ธฐ๋ณธ๊ฐ๊ณผ ์๋ฒ์ ๋ํ ์ฒ๋ฆฌ๊ณผ ๊ท์ฐฎ์ ๊ฒ๋ค์ด
์๋นํ ํธ๋ฆฌํ๊ฒ ๋๊ปด์ก์ต๋๋ค.
๊ฐ๋ฐํ๋ฉฐ ์์ ๋ด์ฉ์์ ์กฐ๊ธ ๋ ์ถ๊ฐ์ ์ผ๋ก ๊ณต์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.