1 /*
2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4 @file:OptIn(ExperimentalSerializationApi::class)
5
6 package kotlinx.serialization.json.internal
7
8 import kotlinx.serialization.*
9 import kotlinx.serialization.descriptors.*
10 import kotlinx.serialization.internal.*
11 import kotlinx.serialization.json.*
12 import kotlinx.serialization.modules.*
13 import kotlin.jvm.*
14
15 @Suppress("UNCHECKED_CAST")
encodePolymorphicallynull16 internal inline fun <T> JsonEncoder.encodePolymorphically(
17 serializer: SerializationStrategy<T>,
18 value: T,
19 ifPolymorphic: (String) -> Unit
20 ) {
21 if (json.configuration.useArrayPolymorphism) {
22 serializer.serialize(this, value)
23 return
24 }
25 val isPolymorphicSerializer = serializer is AbstractPolymorphicSerializer<*>
26 val needDiscriminator =
27 if (isPolymorphicSerializer) {
28 json.configuration.classDiscriminatorMode != ClassDiscriminatorMode.NONE
29 } else {
30 when (json.configuration.classDiscriminatorMode) {
31 ClassDiscriminatorMode.NONE, ClassDiscriminatorMode.POLYMORPHIC /* already handled in isPolymorphicSerializer */ -> false
32 ClassDiscriminatorMode.ALL_JSON_OBJECTS -> serializer.descriptor.kind.let { it == StructureKind.CLASS || it == StructureKind.OBJECT }
33 }
34 }
35 val baseClassDiscriminator = if (needDiscriminator) serializer.descriptor.classDiscriminator(json) else null
36 val actualSerializer: SerializationStrategy<T> = if (isPolymorphicSerializer) {
37 val casted = serializer as AbstractPolymorphicSerializer<Any>
38 requireNotNull(value) { "Value for serializer ${serializer.descriptor} should always be non-null. Please report issue to the kotlinx.serialization tracker." }
39 val actual = casted.findPolymorphicSerializer(this, value)
40 if (baseClassDiscriminator != null) validateIfSealed(serializer, actual, baseClassDiscriminator)
41 checkKind(actual.descriptor.kind)
42 actual as SerializationStrategy<T>
43 } else serializer
44
45 if (baseClassDiscriminator != null) ifPolymorphic(baseClassDiscriminator)
46 actualSerializer.serialize(this, value)
47 }
48
validateIfSealednull49 private fun validateIfSealed(
50 serializer: SerializationStrategy<*>,
51 actualSerializer: SerializationStrategy<*>,
52 classDiscriminator: String
53 ) {
54 if (serializer !is SealedClassSerializer<*>) return
55 @Suppress("DEPRECATION_ERROR")
56 if (classDiscriminator in actualSerializer.descriptor.jsonCachedSerialNames()) {
57 val baseName = serializer.descriptor.serialName
58 val actualName = actualSerializer.descriptor.serialName
59 error(
60 "Sealed class '$actualName' cannot be serialized as base class '$baseName' because" +
61 " it has property name that conflicts with JSON class discriminator '$classDiscriminator'. " +
62 "You can either change class discriminator in JsonConfiguration, " +
63 "rename property with @SerialName annotation or fall back to array polymorphism"
64 )
65 }
66 }
67
checkKindnull68 internal fun checkKind(kind: SerialKind) {
69 if (kind is SerialKind.ENUM) error("Enums cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead")
70 if (kind is PrimitiveKind) error("Primitives cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead")
71 if (kind is PolymorphicKind) error("Actual serializer for polymorphic cannot be polymorphic itself")
72 }
73
decodeSerializableValuePolymorphicnull74 internal fun <T> JsonDecoder.decodeSerializableValuePolymorphic(deserializer: DeserializationStrategy<T>): T {
75 // NB: changes in this method should be reflected in StreamingJsonDecoder#decodeSerializableValue
76 if (deserializer !is AbstractPolymorphicSerializer<*> || json.configuration.useArrayPolymorphism) {
77 return deserializer.deserialize(this)
78 }
79 val discriminator = deserializer.descriptor.classDiscriminator(json)
80
81 val jsonTree = cast<JsonObject>(decodeJsonElement(), deserializer.descriptor)
82 val type = jsonTree[discriminator]?.jsonPrimitive?.contentOrNull // differentiate between `"type":"null"` and `"type":null`.
83 @Suppress("UNCHECKED_CAST")
84 val actualSerializer =
85 try {
86 deserializer.findPolymorphicSerializer(this, type)
87 } catch (it: SerializationException) { // Wrap SerializationException into JsonDecodingException to preserve input
88 throw JsonDecodingException(-1, it.message!!, jsonTree.toString())
89 } as DeserializationStrategy<T>
90 return json.readPolymorphicJson(discriminator, jsonTree, actualSerializer)
91 }
92
classDiscriminatornull93 internal fun SerialDescriptor.classDiscriminator(json: Json): String {
94 // Plain loop is faster than allocation of Sequence or ArrayList
95 // We can rely on the fact that only one JsonClassDiscriminator is present —
96 // compiler plugin checked that.
97 for (annotation in annotations) {
98 if (annotation is JsonClassDiscriminator) return annotation.discriminator
99 }
100 return json.configuration.classDiscriminator
101 }
102
103