Builtin classes
Introducció
Section titled “Introducció”A continuació anem a veure tots els tipus que es serialitzen per defecte.
Primitives
Section titled “Primitives”La serialització de Kotlin té aquest deu “primitives”: Boolean
, Byte
, Short
, Int
, Long
, Float
, Double
, Char
, String
, i enumeracions.
Els tipus Boolean
, Char
i String
es poden utilitzar sense cap tipus de consideració al respecte, però els altres necessiten algunes aclaracions.
Números
Section titled “Números”Kotlin té 4 tipus per representar números enters i 2 tipus per representar números en coma flotat, i com hem dit abans, tots són serialitzables.
@Serializableclass Data( val answer: Int, val pi: Double)
fun main() { val data = Data(42, PI) println(Json.encodeToString(data))}
Pots veure que aquests números es codifiquen sense problemes en JSON:
{ "answer": 42, "pi": 3.141592653589793}
Codificar un Long
Section titled “Codificar un Long”Hi ha llenguatges de programació que tenen més tipus, i altres menys, però el que ens interessa en el procés de serialització és el tipus que pot representar el format al qual codifiquem les dades.
JSON utilitza els tipus de Javascript, i Javascritpt només té el tipus number
que equival a un Double
.
Ja saps que qualsevol valor de tipus Byte
, Short
, Int
o Float
es pot representar amb un Double
, peró no tots els valors de tipus Long
es poden representar amb un Double
.
Per defecte, un Long
amb un valor molt gran es codifica tal qual:
TODO els dos literals coincidents
@Serializableclass Data(val signature: Long)
fun main() { val data = Data(0x1CAFE2FEED0BABE0) println(Json.encodeToString(data))}
Pot veure que signature
es codifica com un número encara que un number
(o un Double
) no pugui representar aquest número:
{ "signature": 2067120338512882656 }
Això no representa cap problema sempre i quant aquest número no el decodifiquin mètodes natius Javascript (Kotlin/JS no té problemes).
En aquests casos, pots anotar una propietat amb LongAsStringSerializer per representar el número amb un string i no perdre precisió.
@Serializableclass Data( @Serializable(with=LongAsStringSerializer::class) val signature: Long)
fun main() { val data = Data(0x1CAFE2FEED0BABE0) println(Json.encodeToString(data))}
Pots veure que ara el valor de signature
no és un number
sinó un string
:
{ "signature": "2067120338512882656" }
Descodificar números
Section titled “Descodificar números”El motiu d’utilizar tipus com Byte
, Short
, Int
o Float
en processadors de 64 bits es estalviar espai en memòria principal o secundària.
Encara que un valor tingui un tipus Byte
aquest valor és processa en la CPU igual que si fos de tipus Long
: el temps de processament és el mateix.
I en dades d’intercanvi, sobretot quan has de descodificar dades, utilitzar aquests tipus no aporta cap benefici i molts problemes.
El dos únics tipus que has d’utlitzar per llegir dades JSON són Int
i Float
.
TODO: demostra el problema , classe amb short
, etc. i …
Enum classes
Section titled “Enum classes”En principi, totes les classes d’enumeració es poden serialitzar sense haver de marcar-les amb l’anotació @Serializable.
A continuació tens un exemple:
enum class Status { CLOSED, SUPPORTED }
@Serializableclass Project(val name: String, val status: Status)
fun main() { val data = Project("dev.xtec.data", Status.SUPPORTED) println(Json.encodeToString(data))}
En JSON, una enumeració es codifica amb un string amb el nom del discriminador de l’enum
:
{ "name": "dev.xtec.data", "status": "SUPPORTED" }
Però hi ha dues exepcions!
1.- A Kotlin/JS i Kotlin/Native has de marcar la classe enum
amb l’anotació @Serializable
si la vols utilitzar com a objecte arrel en el procés de serialització.
encodeToString<Status>(Status.SUPPORTED).
2.- Vols canviar el nom que es fa sevir pel discriminador de l’enum quan es serialitza amb l’anotació `@SerialName.
En aquest cas també has d’anotar la classe amb @Serializable
:
@Serializableenum class Status { CLOSED, @SerialName("maintained") SUPPORTED }
@Serializableclass Project(val name: String, val status: Status)
fun main() { val data = Project("dev.xtec.data", Status.SUPPORTED) println(Json.encodeToString(data))}
Pots veure que ara el valor de status
és “maintained” enlloc de “SUPPORTED”:
{ "name":"dev.xtec.data", "status": "maintained" }
Objectes compostos
Section titled “Objectes compostos”En Kotlin, els tipus primaris boolean
, byte
, etc. són objectes simples.
Els objectes compostos estan formats per objectes simple o compostos de manera recursiva definits per la classe corresponent.
Nomes les classes de la biblioteca estàndard de Kotlin que veurem a continuació es poden serialitzar per defecte.
Pair i triple
Section titled “Pair i triple”Les classes de dades simples Pair i Triple de la biblioteca estàndard de Kotlin són serialitzables.
@Serializableclass Project(val name: String)
fun main() { val pair = 1 to Project("dev.xtec.data") println(Json.encodeToString(pair))}
Pots veure que un objecte de tipus Pair
es serialitza com un objecte json amb dos propietats: first
i second
.
{ "first": 1, "second": { "name":"dev.xtec.data"} }
Col.leccions
Section titled “Col.leccions”Totes les col.leccions en que els seus elements es poden serialitzar es poden serialitzar.
Codificar
Section titled “Codificar”Totes les col.leccions, execepte Map
, es representen amb un “array” JSON.
Per exemple, una List
en que tots els seus elements són serialitzables es pot serialitzar.
@Serializableclass Project(val name: String)
fun main() { val list = listOf( Project("kotlinx.serialization"), Project("kotlinx.coroutines") ) println(Json.encodeToString(list))}
Tal com hem explicat al principi, la llista es representa com un array JSON:
[{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
Pots veure que un Set
també es poden serialitzar:
@Serializableclass Project(val name: String)
fun main() { val set = setOf( Project("kotlinx.serialization"), Project("kotlinx.coroutines") ) println(Json.encodeToString(set))}
I el Set
també es representa com un array JSON:
[{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
Decodificar
Section titled “Decodificar”JSON només té el tipus array
i totes les col.leccions es codifiquen com arrays.
Per tant, al decodificar un string JSON el tipus de la col.leció el determina el tipus de la propietat de la classe o el paràmetre de tipus de la funció de descodificació (TODO posar exemple).
A continuació tens un exemple en que es mostra com el mateix array JSON es deserialitza en dues propietats amb tipus diferent:
@Serializabledata class Data( val a: List<Int>, val b: Set<Int>)
fun main() { val data = Json.decodeFromString<Data>(""" { "a": [42, 42], "b": [42, 42] } """) println(data)}
Com que la propietat data.b
és un Set
, els valors duplicats no apareixen:
Data(a=[42, 42], b=[42])
Un Map
amb claus primitives o enumeracions, i valors serialitzables es pot serialitzar
@Serializableclass Project(val name: String)
fun main() { val map = mapOf( 1 to Project("kotlinx.serialization"), 2 to Project("kotlinx.coroutines") ) println(Json.encodeToString(map))}
A diferència del reste de col.leccions, un Map
es codifica com un objecte JSON (un objecte JSON equival a un map).
L’única diferència és que les claus sempre es codifiquen en string tal com pots veure en el resultat:
{ "1": { "name": "kotlinx.serialization" }, "2": { "name": "kotlinx.coroutines" }}
Duration
Section titled “Duration”La classe Duration
també és serialitzable:
fun main() { val duration = 1000.toDuration(DurationUnit.SECONDS) println(Json.encodeToString(duration))}
La durada es serialitza com una string en el format ISO-8601-2:
"PT16M40S"
Objectes Unit i singletons
Section titled “Objectes Unit i singletons”El tipus Unit
també es serialitzable.
Unit
és un singleton object i es gestiona igual que els altres objectes.
Conceptualment, un singleton és una classe amb només una instància, el que significa que l’estat no defineix l’objecte, però l’objecte defineix el seu estat.
A JSON, els objectes es serialitzen com a estructures buides:
@Serializableobject SerializationVersion { val libraryVersion: String = "1.0.0"}
fun main() { println(Json.encodeToString(SerializationVersion)) println(Json.encodeToString(Unit))}
Tot i que pot semblar inútil a primera vista, això és útil per a la serialització de classes “sealed” com explicarem a TODO Polymorphism.Objects:
{}{}
Nothing
Section titled “Nothing”Per defecte, Nothing
és una classe serialitzable.
Tanmateix, com que no hi ha instàncies d’aquesta classe, és impossible codificar o descodificar els seus valors; qualsevol intent provocarà una excepció.
Aquest serialitzador s’utilitza quan sintàcticament es necessita algun tipus, però en realitat no s’utilitza en la serialització.
Per exemple, quan s’utilitzen classes base polimòrfiques parametritzades:
@Serializablesealed class ParametrizedParent<out R> { @Serializable data class ChildWithoutParameter(val value: Int) : ParametrizedParent<Nothing>()}
fun main() { println(Json.encodeToString(ParametrizedParent.ChildWithoutParameter(42)))}```
Quan es codifica, el serialitzador per `Nothing` no s'ha utilitzat:
```json{ "value": 42 }```