• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 @file:Suppress("LeakingThis")
6 @file:OptIn(ExperimentalSerializationApi::class)
7 
8 package kotlinx.serialization.json.internal
9 
10 import kotlinx.serialization.*
11 import kotlinx.serialization.descriptors.*
12 import kotlinx.serialization.encoding.*
13 import kotlinx.serialization.internal.*
14 import kotlinx.serialization.json.*
15 import kotlinx.serialization.modules.*
16 import kotlin.jvm.*
17 
18 @JsonFriendModuleApi
readJsonnull19 public fun <T> readJson(json: Json, element: JsonElement, deserializer: DeserializationStrategy<T>): T {
20     val input = when (element) {
21         is JsonObject -> JsonTreeDecoder(json, element)
22         is JsonArray -> JsonTreeListDecoder(json, element)
23         is JsonLiteral, JsonNull -> JsonPrimitiveDecoder(json, element as JsonPrimitive)
24     }
25     return input.decodeSerializableValue(deserializer)
26 }
27 
readPolymorphicJsonnull28 internal fun <T> Json.readPolymorphicJson(
29     discriminator: String,
30     element: JsonObject,
31     deserializer: DeserializationStrategy<T>
32 ): T {
33     return JsonTreeDecoder(this, element, discriminator, deserializer.descriptor).decodeSerializableValue(deserializer)
34 }
35 
36 private sealed class AbstractJsonTreeDecoder(
37     override val json: Json,
38     open val value: JsonElement
39 ) : NamedValueDecoder(), JsonDecoder {
40 
41     override val serializersModule: SerializersModule
42         get() = json.serializersModule
43 
44     @JvmField
45     protected val configuration = json.configuration
46 
<lambda>null47     protected fun currentObject() = currentTagOrNull?.let { currentElement(it) } ?: value
48 
decodeJsonElementnull49     override fun decodeJsonElement(): JsonElement = currentObject()
50 
51     override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
52         return decodeSerializableValuePolymorphic(deserializer)
53     }
54 
composeNamenull55     override fun composeName(parentName: String, childName: String): String = childName
56 
57     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
58         val currentObject = currentObject()
59         return when (descriptor.kind) {
60             StructureKind.LIST, is PolymorphicKind -> JsonTreeListDecoder(json, cast(currentObject, descriptor))
61             StructureKind.MAP -> json.selectMapMode(
62                 descriptor,
63                 { JsonTreeMapDecoder(json, cast(currentObject, descriptor)) },
64                 { JsonTreeListDecoder(json, cast(currentObject, descriptor)) }
65             )
66             else -> JsonTreeDecoder(json, cast(currentObject, descriptor))
67         }
68     }
69 
endStructurenull70     override fun endStructure(descriptor: SerialDescriptor) {
71         // Nothing
72     }
73 
decodeNotNullMarknull74     override fun decodeNotNullMark(): Boolean = currentObject() !is JsonNull
75 
76     protected fun getPrimitiveValue(tag: String): JsonPrimitive {
77         val currentElement = currentElement(tag)
78         return currentElement as? JsonPrimitive ?: throw JsonDecodingException(
79             -1,
80             "Expected JsonPrimitive at $tag, found $currentElement", currentObject().toString()
81         )
82     }
83 
currentElementnull84     protected abstract fun currentElement(tag: String): JsonElement
85 
86     override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int =
87         enumDescriptor.getJsonNameIndexOrThrow(json, getPrimitiveValue(tag).content)
88 
89     override fun decodeTaggedNull(tag: String): Nothing? = null
90 
91     override fun decodeTaggedNotNullMark(tag: String): Boolean = currentElement(tag) !== JsonNull
92 
93     override fun decodeTaggedBoolean(tag: String): Boolean {
94         return getPrimitiveValue(tag).primitive("boolean", JsonPrimitive::booleanOrNull)
95     }
96 
<lambda>null97     override fun decodeTaggedByte(tag: String) = getPrimitiveValue(tag).primitive("byte") {
98         val result = int
99         if (result in Byte.MIN_VALUE..Byte.MAX_VALUE) result.toByte()
100         else null
101     }
102 
<lambda>null103     override fun decodeTaggedShort(tag: String) = getPrimitiveValue(tag).primitive("short") {
104         val result = int
105         if (result in Short.MIN_VALUE..Short.MAX_VALUE) result.toShort()
106         else null
107     }
108 
<lambda>null109     override fun decodeTaggedInt(tag: String) = getPrimitiveValue(tag).primitive("int") { int }
<lambda>null110     override fun decodeTaggedLong(tag: String) = getPrimitiveValue(tag).primitive("long") { long }
111 
decodeTaggedFloatnull112     override fun decodeTaggedFloat(tag: String): Float {
113         val result = getPrimitiveValue(tag).primitive("float") { float }
114         val specialFp = json.configuration.allowSpecialFloatingPointValues
115         if (specialFp || result.isFinite()) return result
116         throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
117     }
118 
decodeTaggedDoublenull119     override fun decodeTaggedDouble(tag: String): Double {
120         val result = getPrimitiveValue(tag).primitive("double") { double }
121         val specialFp = json.configuration.allowSpecialFloatingPointValues
122         if (specialFp || result.isFinite()) return result
123         throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
124     }
125 
<lambda>null126     override fun decodeTaggedChar(tag: String): Char = getPrimitiveValue(tag).primitive("char") { content.single() }
127 
primitivenull128     private inline fun <T : Any> JsonPrimitive.primitive(primitive: String, block: JsonPrimitive.() -> T?): T {
129         try {
130             return block() ?: unparsedPrimitive(primitive)
131         } catch (e: IllegalArgumentException) {
132             unparsedPrimitive(primitive)
133         }
134     }
135 
unparsedPrimitivenull136     private fun unparsedPrimitive(primitive: String): Nothing {
137         throw JsonDecodingException(-1, "Failed to parse literal as '$primitive' value", currentObject().toString())
138     }
139 
decodeTaggedStringnull140     override fun decodeTaggedString(tag: String): String {
141         val value = getPrimitiveValue(tag)
142         if (!json.configuration.isLenient) {
143             val literal = value.asLiteral("string")
144             if (!literal.isString) throw JsonDecodingException(
145                 -1, "String literal for key '$tag' should be quoted.\n$lenientHint", currentObject().toString()
146             )
147         }
148         if (value is JsonNull) throw JsonDecodingException(-1, "Unexpected 'null' value instead of string literal", currentObject().toString())
149         return value.content
150     }
151 
asLiteralnull152     private fun JsonPrimitive.asLiteral(type: String): JsonLiteral {
153         return this as? JsonLiteral ?: throw JsonDecodingException(-1, "Unexpected 'null' literal when non-nullable $type was expected")
154     }
155 
decodeTaggedInlinenull156     override fun decodeTaggedInline(tag: String, inlineDescriptor: SerialDescriptor): Decoder =
157         if (inlineDescriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(StringJsonLexer(getPrimitiveValue(tag).content), json)
158         else super.decodeTaggedInline(tag, inlineDescriptor)
159 
160     override fun decodeInline(descriptor: SerialDescriptor): Decoder {
161         return if (currentTagOrNull != null) super.decodeInline(descriptor)
162         else JsonPrimitiveDecoder(json, value).decodeInline(descriptor)
163     }
164 }
165 
166 private class JsonPrimitiveDecoder(json: Json, override val value: JsonElement) : AbstractJsonTreeDecoder(json, value) {
167 
168     init {
169         pushTag(PRIMITIVE_TAG)
170     }
171 
decodeElementIndexnull172     override fun decodeElementIndex(descriptor: SerialDescriptor): Int = 0
173 
174     override fun currentElement(tag: String): JsonElement {
175         require(tag === PRIMITIVE_TAG) { "This input can only handle primitives with '$PRIMITIVE_TAG' tag" }
176         return value
177     }
178 }
179 
180 private open class JsonTreeDecoder(
181     json: Json,
182     override val value: JsonObject,
183     private val polyDiscriminator: String? = null,
184     private val polyDescriptor: SerialDescriptor? = null
185 ) : AbstractJsonTreeDecoder(json, value) {
186     private var position = 0
187     private var forceNull: Boolean = false
188     /*
189      * Checks whether JSON has `null` value for non-null property or unknown enum value for enum property
190      */
coerceInputValuenull191     private fun coerceInputValue(descriptor: SerialDescriptor, index: Int, tag: String): Boolean =
192         json.tryCoerceValue(
193             descriptor, index,
194             { currentElement(tag) is JsonNull },
<lambda>null195             { (currentElement(tag) as? JsonPrimitive)?.contentOrNull }
196         )
197 
198     @Suppress("INVISIBLE_MEMBER")
decodeElementIndexnull199     override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
200         while (position < descriptor.elementsCount) {
201             val name = descriptor.getTag(position++)
202             val index = position - 1
203             forceNull = false
204             if ((name in value || absenceIsNull(descriptor, index))
205                 && (!configuration.coerceInputValues || !coerceInputValue(descriptor, index, name))
206             ) {
207                 return index
208             }
209         }
210         return CompositeDecoder.DECODE_DONE
211     }
212 
absenceIsNullnull213     private fun absenceIsNull(descriptor: SerialDescriptor, index: Int): Boolean {
214         forceNull = !json.configuration.explicitNulls
215                 && !descriptor.isElementOptional(index) && descriptor.getElementDescriptor(index).isNullable
216         return forceNull
217     }
218 
decodeNotNullMarknull219     override fun decodeNotNullMark(): Boolean {
220         return !forceNull && super.decodeNotNullMark()
221     }
222 
elementNamenull223     override fun elementName(descriptor: SerialDescriptor, index: Int): String {
224         val strategy = descriptor.namingStrategy(json)
225         val baseName = descriptor.getElementName(index)
226         if (strategy == null) {
227             if (!configuration.useAlternativeNames) return baseName
228             // Fast path, do not go through ConcurrentHashMap.get
229             // Note, it blocks ability to detect collisions between the primary name and alternate,
230             // but it eliminates a significant performance penalty (about -15% without this optimization)
231             if (baseName in value.keys) return baseName
232         }
233         // Slow path
234         val deserializationNamesMap = json.deserializationNamesMap(descriptor)
235         value.keys.find { deserializationNamesMap[it] == index }?.let {
236             return it
237         }
238 
239         val fallbackName = strategy?.serialNameForJson(
240             descriptor,
241             index,
242             baseName
243         ) // Key not found exception should be thrown with transformed name, not original
244         return fallbackName ?: baseName
245     }
246 
currentElementnull247     override fun currentElement(tag: String): JsonElement = value.getValue(tag)
248 
249     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
250         // polyDiscriminator needs to be preserved so the check for unknown keys
251         // in endStructure can filter polyDiscriminator out.
252         if (descriptor === polyDescriptor) {
253             return JsonTreeDecoder(
254                 json, cast(currentObject(), polyDescriptor), polyDiscriminator, polyDescriptor
255             )
256         }
257 
258         return super.beginStructure(descriptor)
259     }
260 
endStructurenull261     override fun endStructure(descriptor: SerialDescriptor) {
262         if (configuration.ignoreUnknownKeys || descriptor.kind is PolymorphicKind) return
263         // Validate keys
264         val strategy = descriptor.namingStrategy(json)
265 
266         @Suppress("DEPRECATION_ERROR")
267         val names: Set<String> = when {
268             strategy == null && !configuration.useAlternativeNames -> descriptor.jsonCachedSerialNames()
269             strategy != null -> json.deserializationNamesMap(descriptor).keys
270             else -> descriptor.jsonCachedSerialNames() + json.schemaCache[descriptor, JsonDeserializationNamesKey]?.keys.orEmpty()
271         }
272 
273         for (key in value.keys) {
274             if (key !in names && key != polyDiscriminator) {
275                 throw UnknownKeyException(key, value.toString())
276             }
277         }
278     }
279 }
280 
281 private class JsonTreeMapDecoder(json: Json, override val value: JsonObject) : JsonTreeDecoder(json, value) {
282     private val keys = value.keys.toList()
283     private val size: Int = keys.size * 2
284     private var position = -1
285 
elementNamenull286     override fun elementName(descriptor: SerialDescriptor, index: Int): String {
287         val i = index / 2
288         return keys[i]
289     }
290 
decodeElementIndexnull291     override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
292         while (position < size - 1) {
293             position++
294             return position
295         }
296         return CompositeDecoder.DECODE_DONE
297     }
298 
currentElementnull299     override fun currentElement(tag: String): JsonElement {
300         return if (position % 2 == 0) JsonPrimitive(tag) else value.getValue(tag)
301     }
302 
endStructurenull303     override fun endStructure(descriptor: SerialDescriptor) {
304         // do nothing, maps do not have strict keys, so strict mode check is omitted
305     }
306 }
307 
308 private class JsonTreeListDecoder(json: Json, override val value: JsonArray) : AbstractJsonTreeDecoder(json, value) {
309     private val size = value.size
310     private var currentIndex = -1
311 
elementNamenull312     override fun elementName(descriptor: SerialDescriptor, index: Int): String = index.toString()
313 
314     override fun currentElement(tag: String): JsonElement {
315         return value[tag.toInt()]
316     }
317 
decodeElementIndexnull318     override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
319         while (currentIndex < size - 1) {
320             currentIndex++
321             return currentIndex
322         }
323         return CompositeDecoder.DECODE_DONE
324     }
325 }
326