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
- Non-null check/non-null overwrite, which will not overwrite the default value of the construction parameter
- Solve generic erasing problems, directly serialize/deserialize List/Map/Pair, etc
- Non-reflective high performance
- Support multiple formats (ProtoBuf/CBOR/ custom)
- Annotation data classes automatically generate sequences, and manually construct sequences
- Simple and elegant code
- 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.
- Inline generic functions have automatic resolution of object types. In general, this function can be used directly
- 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
- Only properties support serialization (that is, including setters and getters)
- 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
- Private constructors that expose other constructors, so Serialization serializes the exposed constructor properties
- Serialization fails when a data class field has more attributes than a JSON field, unless the additional attributes have default values (
@Required
Force JSON to match the field) - If there is a loop structure field during serialization, it will cause stack overflow. It is recommended that you ignore and exclude this field
- When a JSON field overrides an attribute value, the default value of the attribute value is a function that is not called
- 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.NaN This special floating point type |
false |
classDiscriminator = “#class” | Using the attribute name as the value,#class For 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
-
Bound data class
@Serializable(with = ColorAsStringSerializer::class) class Color(val rgb: Int) Copy the code
-
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
-
Function parameters
public fun <T> decodeFromJsonElement(deserializer: DeserializationStrategy<T>, element: JsonElement): T Copy the code
-
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 one
serializer
function - 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 to
serializer
Each 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