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