Serialization is JetBrains open source parsing library with multiple platforms and support for multiple parsing formats. It is one of the most powerful Serialization parsing libraries on Kotlin

Documents:

  • Doc
  • guide
  • GitHub

Serialization has its own advantages

  1. Non-null check/non-null overwrite, which will not overwrite the default value of the construction parameter
  2. Solve generic erasing problems, directly serialize/deserialize List/Map/Pair, etc
  3. Non-reflective high performance
  4. Support multiple formats (ProtoBuf/CBOR/ custom)
  5. Annotation data classes automatically generate sequences, and manually construct sequences
  6. Simple and elegant code
  7. The dynamic analysis

Serialization, because it uses Kotlin’s Type instead of Java, currently only the Net Network request library supports its converters. The implementation can specify any generic parsing result: access documents.

Net allows you to write the most elegant request code

The installation

The project build. Gradle

classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
// Use the same version as the Kotlin plugin
Copy the code

module build.gradle

apply plugin: 'kotlinx-serialization'

implementation "Org. Jetbrains. Kotlinx: kotlinx serialization - json: 1.2.0"
Copy the code

JSON using

The key class
Json A singleton object used to parse the CONFIGURATION of JSON
Json.Default Singleton object configured by default

function

Json functions are divided into inline generic functions and plain generic functions.

  1. Inline generic functions have automatic resolution of object types. In general, this function can be used directly
  2. Normal generics require that a parser be specified. Should be for parsing encapsulation
function describe
encodeToString Serialize to string
encodeToJsonElement Serialize to JsonElement
decodeFromString Deserialize from a string
decodeFromJsonElement Deserialize from JsonElement

The sample

/ / the serialization
val json:String = Json.encodeToString(Model("Eddie Peng".23))

// deserialize
val json = {"name":" peng Yuyan ","age":33}""
val model = Json.decodeFromString<Model>(json)
Copy the code

serialization

  1. Only properties support serialization (that is, including setters and getters)
  2. Default values are not serialized, even for nullable attributes
@Serializable
class Project(val name: String, val language: String)

fun main(a) {
    val data = Project("kotlinx.serialization"."Kotlin")
    println(Json.encodeToString(data))}Copy the code

deserialization

@Serializable 
class Project(val name: String, val language: String)

fun main(a) {
    val data = Project("kotlinx.serialization"."Kotlin")
    println(Json.encodeToString(data))}Copy the code
  1. Private constructors that expose other constructors, so Serialization serializes the exposed constructor properties
  2. Serialization fails when a data class field has more attributes than a JSON field, unless the additional attributes have default values (@RequiredForce JSON to match the field)
  3. If there is a loop structure field during serialization, it will cause stack overflow. It is recommended that you ignore and exclude this field
  4. When a JSON field overrides an attribute value, the default value of the attribute value is a function that is not called
  5. JSON overwriting properties with default values is an error

annotations

annotations Modified position describe
@Transient field Ignore the field
@Required field The mandatory default parameters also match the JSON field
@SerialName field The modifier class is the name of the person who specified the sequence, and the modifier field is the name specified in JSON
@JsonNames field You can specify multiple names for a field without invalidating the field name
@SerialInfo class Allows the compiler to save comment information to SerialDescriptor, usinggetElementAnnotations
@Serializer class The specified argument is the target class, which creates the serializer with its modifier class

model

  • Unsigned types/inline classes are not currently supported
  • Singleton objects do not support serialization (Unit is a singleton) and the serialization result is{}
  • Attributes of the parent class are not serialized
  • Objects are serialized only if they are assigned (assignment null is also involved), nor if there is a default value (unless the @required modifier is used).
  • Neither delegate field /Val field participates in serialization. Serialized only if you have both set/get (construction argument allows val)
  • The fields of the private constructor are also serialized. If the constructor parameter is not val or var, it does not participate in the serialization
@Serializable
class Model(var name: String, var age: Int) {
    var height: Long? = 23 // No assignment will never serialize
}
Copy the code

Or declare a sequence directly in a file

@file:UseSerializers(DateAsLongSerializer::class)
Copy the code

Custom serializer is both a custom serializer

@Serializable(with = BoxSerializer::class)
data class Box<T>(val contents: T) 
Copy the code

We can use the @serializer modifier for Serializer when the class cannot be decorated

@Serializer(forClass = Project::class)
object ProjectSerializer
Copy the code

Not only constructor parameters are sequenced, but only properties that have getter/setter functions are involved

The parent class is decorated by @serializable, and its subclasses must also be decorated

@Serializable
open class Project(val name: String)

class OwnedProject(name: String, val owner: String) : Project(name)

fun main(a) {
    val data: Project = OwnedProject("kotlinx.coroutines"."kotlin")
    println(Json.encodeToString(data))
	val data = OwnedProject("kotlinx.coroutines"."kotlin") // Throw an exception
}  
Copy the code

Only sealed classes allow abstract attributes, not non-sealed classes

@Serializable
sealed class Project {
    abstract val name: String
}
            
@Serializable
class OwnedProject(override val name: String, val owner: String) : Project()

fun main(a) {
    val data: Project = OwnedProject("kotlinx.coroutines"."kotlin")
    println(Json.encodeToString(data))}Copy the code

The value of the type key can use the attribute name

@Serializable         
@SerialName("owned")
class OwnedProject(override val name: String, val owner: String) : Project()
Copy the code

A type field is added by default when the collection is polymorphic

Copy the code

Json configuration

Build a Json instance object using Json{}

val format = Json { prettyPrint = true }

@Serializable 
data class Project(val name: String, val language: String)

fun main(a) {                                      
    val data = Project("kotlinx.serialization"."Kotlin")
    println(format.encodeToString(data))}Copy the code
options describe The default value
prettyPrint: Boolean Generate the typeset JSON false
prettyPrintIndent: String Specifies the indent string for typesetting false
isLenient: Boolean Key values are allowed to be wrapped in non-double quotes false
ignoreUnknownKeys: Boolean Data classes that allow deserialization are missing fields false
useAlternativeNames: Boolean Whether to enable the @jsonName annotation. If not, you are advised to disable this configuration true
coerceInputValues: Boolean Empty and unknown enumerations do not override property defaults false
allowStructuredMapKeys: Boolean Enable Map serialization. By default, maps cannot be serialized false
allowSpecialFloatingPointValues: Boolean Allow serializationDouble.NaNThis special floating point type false
classDiscriminator = “#class” Using the attribute name as the value,#classFor custom IDE key names
serializersModule = moduleForDate
encodeDefaults: Boolean Serialization or not The default value supports the (non-abstract) serialization of attributes of the parent class false

Enable Map serialization (by default, maps cannot be serialized)

val format = Json { allowStructuredMapKeys = true }

@Serializable 
data class Project(val name: String)
    
fun main(a) {             
    val map = mapOf(
        Project("kotlinx.serialization") to "Serialization",
        Project("kotlinx.coroutines") to "Coroutines"
    )
    println(format.encodeToString(map))
}
Copy the code

Class description

This automatically adds a field to the serialized JSON that describes the class information of the data model

val format = Json { classDiscriminator = "#class" } // key

@Serializable
sealed class Project {
    abstract val name: String
}
            
@Serializable         
@SerialName("owned") // value
class OwnedProject(override val name: String, val owner: String) : Project()

fun main(a) {
    val data: Project = OwnedProject("kotlinx.coroutines"."kotlin")
    println(format.encodeToString(data))
  	// {"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
}  
Copy the code

Allows specifying floating point values

val data = Data(Double.NaN)
println(format.encodeToString(data))
// {"value":NaN}
Copy the code

Enable default values

Setting coerceInputValues to true assumes the default value rather than the override field if the type is non-null but the JSON value is null.

The default value is also used for unknown enumeration types

JsonElement

Use the json. parseToJsonElement function to parse a JsonElement object that is not deserialized

A subclass describe
JsonPrimitive Primitive type in Kotlin
JsonArray A collection of JsonElements
JsonObject A Map collection of JsonElements
JsonNull Empty type

JsonPrimitive has a set of basic type fetch functions that return String please call content.

function describe
jsonPrimitive Return the original type
jsonObject Return to the Map
jsonArray Returns the collection
jsonNull Returns Null
fun main(a) {
    val element = Json.parseToJsonElement(""" { "name": "kotlinx.serialization", "forks": [{"votes": 42}, {"votes": 9000}, {}] } """)
    val sum = element
        .jsonObject["forks"]!!!!! .jsonArray.sumOf { it.jsonObject["votes"]? .jsonPrimitive? .int ?:0 }
    println(sum)
}
Copy the code

Build JSON

Provides top-level DSL functions to build JSON

fun main(a) {
    val element = buildJsonObject {
        put("name"."kotlinx.serialization")
        putJsonObject("owner") {
            put("name"."kotlin")
        }
        putJsonArray("forks") {
            addJsonObject {
                put("votes".42)
            }
            addJsonObject {
                put("votes".9000)
            }
        }
    }
    println(element)
}
Copy the code

KSerializer

KSerializer belongs to serializer, an interface specification that includes Serialization and deserialization.

You can create a sequencer in the following way

  1. Bound data class

    @Serializable(with = ColorAsStringSerializer::class)
    class Color(val rgb: Int)
    Copy the code
  2. Specifies an object on a sequencer

    // NOT @Serializable
    class Project(val name: String, val language: String)
    
    @Serializer(forClass = Project::class)
    object ProjectSerializer
    // The sequencer can be used without any code logic
    Copy the code
  3. Function parameters

    public fun <T> decodeFromJsonElement(deserializer: DeserializationStrategy<T>, element: JsonElement): T
    Copy the code
  4. Specifies the sequencer used by the class of the current file

    @file:UseSerializers(DateAsLongSerializer::class)
    
    @Serializable          
    class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)
    Copy the code

Automatic sequencer generator

Each class decorated by @serializable has a singleton function serializer() that returns a KSerializer

serialization

  • So you cannot declare to create oneserializerfunction
  • Primitive types have default sequences:Int.serializer()
/ / the serialization
public interface SerializationStrategy<in T> {
    public val descriptor: SerialDescriptor
    public fun serialize(encoder: Encoder, value: T)
}

// deserialize
public interface DeserializationStrategy<T> {
    public val descriptor: SerialDescriptor
    public fun deserialize(decoder: Decoder): T
}

/ / sequence
public interface KSerializer<T> : SerializationStrategy<T>, DeserializationStrategy<T> {
    override val descriptor: SerialDescriptor
}
Copy the code

Encoding and decoding

Encoding and decoding describe
Encoder Serialization root interface
CompositeEncoder
JsonEncoder For JSON serialization
Decoder Deserialize the root interface
CompositeDecoder
JsonDecoder Used for JSON deserialization

Encoder can use encodeStructure to start manual coding

There is a loop inside the Decoder that always calls decodeElementIndex to start decoding and stops when it encounters CompositeDecoder.decode_done

If the data is stored sequentially, we can terminate the loop by using the decodeSequentially function directly

    override fun deserialize(decoder: Decoder): Color =
        decoder.decodeStructure(descriptor) {
            var r = -1
            var g = -1
            var b = -1     
            if (decodeSequentially()) { // sequential decoding protocol
                r = decodeIntElement(descriptor, 0)           
                g = decodeIntElement(descriptor, 1)  
                b = decodeIntElement(descriptor, 2)}else while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> r = decodeIntElement(descriptor, 0)
                    1 -> g = decodeIntElement(descriptor, 1)
                    2 -> b = decodeIntElement(descriptor, 2)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
            require(r in 0.255. && g in 0.255. && b in 0.255.)
            Color((r shl 16) or (g shl 8) or b)
        }
Copy the code

type

Serialization defines a number of top-level functions to create seriizers: BuiltinSerializers. Kt

Sequence,
PairSerializer
MapEntrySerializer
TripleSerializer
*ArraySerializer
ListSerializer
SetSerializer
MapSerializer
LongAsStringSerializer

It basically covers all data types, deserialization does not require the use of sequence default supported types. Enumeration serialization and deserialization require no extra processing

Generic type inference can be used to quickly generate corresponding sequences

val stringToColorMapSerializer: KSerializer<Map<String, Color>> = serializer()
println(stringToColorMapSerializer.descriptor)
Copy the code

Serialization parses maps, and the key is always a string, even a number

@Serializable
class Project(val name: String)

fun main(a) {
    val map = mapOf(
        1 to Project("kotlinx.serialization"),
        2 to Project("kotlinx.coroutines")    
    )
    println(Json.encodeToString(map))
}  
Copy the code

The generic

@Serializable
class Box<T>(val contents: T)

fun main(a) {
    val boxedColorSerializer = Box.serializer(Color.serializer())
    println(boxedColorSerializer.descriptor)
} 
Copy the code
  • The number of generics causes the requirement to be passed toserializerEach generic parameter should have its own sequence

Json serialization

JsonTransformingSerializer belongs to Json parsing. If we need to customize the serializer that parses JSON we can inherit the class implementation function

public abstract class JsonTransformingSerializer<T : Any>(
    private val tSerializer: KSerializer<T>
) : KSerializer<T> {
	// deserialize
  protected open fun transformDeserialize(element: JsonElement): JsonElement = element
	/ / the serialization
	protected open fun transformSerialize(element: JsonElement): JsonElement = element
}
Copy the code

Filter out a value (default values are filtered)

For ease of use and memory savings, parsers typically use singleton objects

fun main(a) {
    val data = Project("kotlinx.serialization"."Kotlin")
    println(Json.encodeToString(data)) // using plugin-generated serializer
    println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
}

@Serializable
class Project(val name: String, val language: String)

object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
    override fun transformSerialize(element: JsonElement): JsonElement =
        // If the key "language" is "Kotlin", filter it out
        JsonObject(element.jsonObject.filterNot {
                (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"})}Copy the code

Polymorphic serialization provides a function to return the concrete serializer

object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
    override fun selectDeserializer(element: JsonElement) = when {
        "owner" in element.jsonObject -> OwnedProject.serializer()
        else -> BasicProject.serializer()
    }
}
Copy the code