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 package kotlinx.serialization.json.internal
6
7 import kotlinx.serialization.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.encoding.*
10 import kotlinx.serialization.encoding.CompositeDecoder.Companion.DECODE_DONE
11 import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
12 import kotlinx.serialization.internal.*
13 import kotlinx.serialization.json.*
14 import kotlinx.serialization.modules.*
15 import kotlin.jvm.*
16
17 /**
18 * [JsonDecoder] which reads given JSON from [AbstractJsonLexer] field by field.
19 */
20 @OptIn(ExperimentalSerializationApi::class)
21 internal open class StreamingJsonDecoder(
22 final override val json: Json,
23 private val mode: WriteMode,
24 @JvmField internal val lexer: AbstractJsonLexer,
25 descriptor: SerialDescriptor,
26 discriminatorHolder: DiscriminatorHolder?
27 ) : JsonDecoder, ChunkedDecoder, AbstractDecoder() {
28
29 // A mutable reference to the discriminator that have to be skipped when in optimistic phase
30 // of polymorphic serialization, see `decodeSerializableValue`
31 internal class DiscriminatorHolder(@JvmField var discriminatorToSkip: String?)
32
trySkipnull33 private fun DiscriminatorHolder?.trySkip(unknownKey: String): Boolean {
34 if (this == null) return false
35 if (discriminatorToSkip == unknownKey) {
36 discriminatorToSkip = null
37 return true
38 }
39 return false
40 }
41
42
43 override val serializersModule: SerializersModule = json.serializersModule
44 private var currentIndex = -1
45 private var discriminatorHolder: DiscriminatorHolder? = discriminatorHolder
46 private val configuration = json.configuration
47
48 private val elementMarker: JsonElementMarker? = if (configuration.explicitNulls) null else JsonElementMarker(descriptor)
49
decodeJsonElementnull50 override fun decodeJsonElement(): JsonElement = JsonTreeReader(json.configuration, lexer).read()
51
52 override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
53 try {
54 /*
55 * This is an optimized path over decodeSerializableValuePolymorphic(deserializer):
56 * dSVP reads the very next JSON tree into a memory as JsonElement and then runs TreeJsonDecoder over it
57 * in order to deal with an arbitrary order of keys, but with the price of additional memory pressure
58 * and CPU consumption.
59 * We would like to provide the best possible performance for data produced by kotlinx.serialization
60 * itself, for that we do the following optimistic optimization:
61 *
62 * 0) Remember current position in the string
63 * 1) Read the very next key of JSON structure
64 * 2) If it matches* the discriminator key, read the value, remember current position
65 * 3) Return the value, recover an initial position
66 * (*) -- if it doesn't match, fallback to dSVP method.
67 */
68 if (deserializer !is AbstractPolymorphicSerializer<*> || json.configuration.useArrayPolymorphism) {
69 return deserializer.deserialize(this)
70 }
71
72 val discriminator = deserializer.descriptor.classDiscriminator(json)
73 val type = lexer.peekLeadingMatchingValue(discriminator, configuration.isLenient)
74 ?: // Fallback to slow path if we haven't found discriminator on first try
75 return decodeSerializableValuePolymorphic<T>(deserializer as DeserializationStrategy<T>)
76
77 @Suppress("UNCHECKED_CAST")
78 val actualSerializer = try {
79 deserializer.findPolymorphicSerializer(this, type)
80 } catch (it: SerializationException) { // Wrap SerializationException into JsonDecodingException to preserve position, path, and input.
81 // Split multiline message from private core function:
82 // core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt:102
83 val message = it.message!!.substringBefore('\n').removeSuffix(".")
84 val hint = it.message!!.substringAfter('\n', missingDelimiterValue = "")
85 lexer.fail(message, hint = hint)
86 } as DeserializationStrategy<T>
87
88 discriminatorHolder = DiscriminatorHolder(discriminator)
89 return actualSerializer.deserialize(this)
90
91 } catch (e: MissingFieldException) {
92 // Add "at path" if and only if we've just caught an exception and it hasn't been augmented yet
93 if (e.message!!.contains("at path")) throw e
94 // NB: we could've use some additional flag marker or augment the stacktrace, but it seemed to be as too much of a burden
95 throw MissingFieldException(e.missingFields, e.message + " at path: " + lexer.path.getPath(), e)
96 }
97 }
98
beginStructurenull99 override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
100 val newMode = json.switchMode(descriptor)
101 lexer.path.pushDescriptor(descriptor)
102 lexer.consumeNextToken(newMode.begin)
103 checkLeadingComma()
104 return when (newMode) {
105 // In fact resets current index that these modes rely on
106 WriteMode.LIST, WriteMode.MAP, WriteMode.POLY_OBJ -> StreamingJsonDecoder(
107 json,
108 newMode,
109 lexer,
110 descriptor,
111 discriminatorHolder
112 )
113 else -> if (mode == newMode && json.configuration.explicitNulls) {
114 this
115 } else {
116 StreamingJsonDecoder(json, newMode, lexer, descriptor, discriminatorHolder)
117 }
118 }
119 }
120
endStructurenull121 override fun endStructure(descriptor: SerialDescriptor) {
122 // If we're ignoring unknown keys, we have to skip all un-decoded elements,
123 // e.g. for object serialization. It can be the case when the descriptor does
124 // not have any elements and decodeElementIndex is not invoked at all
125 if (json.configuration.ignoreUnknownKeys && descriptor.elementsCount == 0) {
126 skipLeftoverElements(descriptor)
127 }
128 if (lexer.tryConsumeComma() && !json.configuration.allowTrailingComma) lexer.invalidTrailingComma("")
129 // First consume the object so we know it's correct
130 lexer.consumeNextToken(mode.end)
131 // Then cleanup the path
132 lexer.path.popDescriptor()
133 }
134
skipLeftoverElementsnull135 private fun skipLeftoverElements(descriptor: SerialDescriptor) {
136 while (decodeElementIndex(descriptor) != DECODE_DONE) {
137 // Skip elements
138 }
139 }
140
decodeNotNullMarknull141 override fun decodeNotNullMark(): Boolean {
142 return !(elementMarker?.isUnmarkedNull ?: false) && !lexer.tryConsumeNull()
143 }
144
decodeNullnull145 override fun decodeNull(): Nothing? {
146 // Do nothing, null was consumed by `decodeNotNullMark`
147 return null
148 }
149
checkLeadingCommanull150 private fun checkLeadingComma() {
151 if (lexer.peekNextToken() == TC_COMMA) {
152 lexer.fail("Unexpected leading comma")
153 }
154 }
155
decodeSerializableElementnull156 override fun <T> decodeSerializableElement(
157 descriptor: SerialDescriptor,
158 index: Int,
159 deserializer: DeserializationStrategy<T>,
160 previousValue: T?
161 ): T {
162 val isMapKey = mode == WriteMode.MAP && index and 1 == 0
163 // Reset previous key
164 if (isMapKey) {
165 lexer.path.resetCurrentMapKey()
166 }
167 // Deserialize the key
168 val value = super.decodeSerializableElement(descriptor, index, deserializer, previousValue)
169 // Put the key to the path
170 if (isMapKey) {
171 lexer.path.updateCurrentMapKey(value)
172 }
173 return value
174 }
175
decodeElementIndexnull176 override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
177 val index = when (mode) {
178 WriteMode.OBJ -> decodeObjectIndex(descriptor)
179 WriteMode.MAP -> decodeMapIndex()
180 else -> decodeListIndex() // Both for LIST and default polymorphic
181 }
182 // The element of the next index that will be decoded
183 if (mode != WriteMode.MAP) {
184 lexer.path.updateDescriptorIndex(index)
185 }
186 return index
187 }
188
decodeMapIndexnull189 private fun decodeMapIndex(): Int {
190 var hasComma = false
191 val decodingKey = currentIndex % 2 != 0
192 if (decodingKey) {
193 if (currentIndex != -1) {
194 hasComma = lexer.tryConsumeComma()
195 }
196 } else {
197 lexer.consumeNextToken(COLON)
198 }
199
200 return if (lexer.canConsumeValue()) {
201 if (decodingKey) {
202 if (currentIndex == -1) lexer.require(!hasComma) { "Unexpected leading comma" }
203 else lexer.require(hasComma) { "Expected comma after the key-value pair" }
204 }
205 ++currentIndex
206 } else {
207 if (hasComma && !json.configuration.allowTrailingComma) lexer.invalidTrailingComma()
208 CompositeDecoder.DECODE_DONE
209 }
210 }
211
212 /*
213 * Checks whether JSON has `null` value for non-null property or unknown enum value for enum property
214 */
coerceInputValuenull215 private fun coerceInputValue(descriptor: SerialDescriptor, index: Int): Boolean = json.tryCoerceValue(
216 descriptor, index,
217 { lexer.tryConsumeNull(it) },
<lambda>null218 { lexer.peekString(configuration.isLenient) },
<lambda>null219 { lexer.consumeString() /* skip unknown enum string*/ }
220 )
221
decodeObjectIndexnull222 private fun decodeObjectIndex(descriptor: SerialDescriptor): Int {
223 // hasComma checks are required to properly react on trailing commas
224 var hasComma = lexer.tryConsumeComma()
225 while (lexer.canConsumeValue()) { // TODO: consider merging comma consumption and this check
226 hasComma = false
227 val key = decodeStringKey()
228 lexer.consumeNextToken(COLON)
229 val index = descriptor.getJsonNameIndex(json, key)
230 val isUnknown = if (index != UNKNOWN_NAME) {
231 if (configuration.coerceInputValues && coerceInputValue(descriptor, index)) {
232 hasComma = lexer.tryConsumeComma()
233 false // Known element, but coerced
234 } else {
235 elementMarker?.mark(index)
236 return index // Known element without coercing, return it
237 }
238 } else {
239 true // unknown element
240 }
241
242 if (isUnknown) { // slow-path for unknown keys handling
243 hasComma = handleUnknown(key)
244 }
245 }
246 if (hasComma && !json.configuration.allowTrailingComma) lexer.invalidTrailingComma()
247
248 return elementMarker?.nextUnmarkedIndex() ?: CompositeDecoder.DECODE_DONE
249 }
250
handleUnknownnull251 private fun handleUnknown(key: String): Boolean {
252 if (configuration.ignoreUnknownKeys || discriminatorHolder.trySkip(key)) {
253 lexer.skipElement(configuration.isLenient)
254 } else {
255 // Here we cannot properly update json path indices
256 // as we do not have a proper SerialDescriptor in our hands
257 lexer.failOnUnknownKey(key)
258 }
259 return lexer.tryConsumeComma()
260 }
261
decodeListIndexnull262 private fun decodeListIndex(): Int {
263 // Prohibit leading comma
264 val hasComma = lexer.tryConsumeComma()
265 return if (lexer.canConsumeValue()) {
266 if (currentIndex != -1 && !hasComma) lexer.fail("Expected end of the array or comma")
267 ++currentIndex
268 } else {
269 if (hasComma && !json.configuration.allowTrailingComma) lexer.invalidTrailingComma("array")
270 CompositeDecoder.DECODE_DONE
271 }
272 }
273
274 /*
275 * The primitives are allowed to be quoted and unquoted
276 * to simplify map key parsing and integrations with third-party API.
277 */
decodeBooleannull278 override fun decodeBoolean(): Boolean {
279 return lexer.consumeBooleanLenient()
280 }
281
decodeBytenull282 override fun decodeByte(): Byte {
283 val value = lexer.consumeNumericLiteral()
284 // Check for overflow
285 if (value != value.toByte().toLong()) lexer.fail("Failed to parse byte for input '$value'")
286 return value.toByte()
287 }
288
decodeShortnull289 override fun decodeShort(): Short {
290 val value = lexer.consumeNumericLiteral()
291 // Check for overflow
292 if (value != value.toShort().toLong()) lexer.fail("Failed to parse short for input '$value'")
293 return value.toShort()
294 }
295
decodeIntnull296 override fun decodeInt(): Int {
297 val value = lexer.consumeNumericLiteral()
298 // Check for overflow
299 if (value != value.toInt().toLong()) lexer.fail("Failed to parse int for input '$value'")
300 return value.toInt()
301 }
302
decodeLongnull303 override fun decodeLong(): Long {
304 return lexer.consumeNumericLiteral()
305 }
306
decodeFloatnull307 override fun decodeFloat(): Float {
308 val result = lexer.parseString("float") { toFloat() }
309 val specialFp = json.configuration.allowSpecialFloatingPointValues
310 if (specialFp || result.isFinite()) return result
311 lexer.throwInvalidFloatingPointDecoded(result)
312 }
313
decodeDoublenull314 override fun decodeDouble(): Double {
315 val result = lexer.parseString("double") { toDouble() }
316 val specialFp = json.configuration.allowSpecialFloatingPointValues
317 if (specialFp || result.isFinite()) return result
318 lexer.throwInvalidFloatingPointDecoded(result)
319 }
320
decodeCharnull321 override fun decodeChar(): Char {
322 val string = lexer.consumeStringLenient()
323 if (string.length != 1) lexer.fail("Expected single char, but got '$string'")
324 return string[0]
325 }
326
decodeStringKeynull327 private fun decodeStringKey(): String {
328 return if (configuration.isLenient) {
329 lexer.consumeStringLenientNotNull()
330 } else {
331 lexer.consumeKeyString()
332 }
333 }
334
decodeStringnull335 override fun decodeString(): String {
336 return if (configuration.isLenient) {
337 lexer.consumeStringLenientNotNull()
338 } else {
339 lexer.consumeString()
340 }
341 }
342
decodeStringChunkednull343 override fun decodeStringChunked(consumeChunk: (chunk: String) -> Unit) {
344 lexer.consumeStringChunked(configuration.isLenient, consumeChunk)
345 }
346
decodeInlinenull347 override fun decodeInline(descriptor: SerialDescriptor): Decoder =
348 if (descriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(lexer, json)
349 else super.decodeInline(descriptor)
350
351 override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
352 return enumDescriptor.getJsonNameIndexOrThrow(json, decodeString(), " at path " + lexer.path.getPath())
353 }
354 }
355
356 @JsonFriendModuleApi // used in json-tests
decodeStringToJsonTreenull357 public fun <T> decodeStringToJsonTree(
358 json: Json,
359 deserializer: DeserializationStrategy<T>,
360 source: String
361 ): JsonElement {
362 val lexer = StringJsonLexer(source)
363 val input = StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor, null)
364 val tree = input.decodeJsonElement()
365 lexer.expectEof()
366 return tree
367 }
368
369 @OptIn(ExperimentalSerializationApi::class)
370 internal class JsonDecoderForUnsignedTypes(
371 private val lexer: AbstractJsonLexer,
372 json: Json
373 ) : AbstractDecoder() {
374 override val serializersModule: SerializersModule = json.serializersModule
decodeElementIndexnull375 override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("unsupported")
376
377 override fun decodeInt(): Int = lexer.parseString("UInt") { toUInt().toInt() }
<lambda>null378 override fun decodeLong(): Long = lexer.parseString("ULong") { toULong().toLong() }
<lambda>null379 override fun decodeByte(): Byte = lexer.parseString("UByte") { toUByte().toByte() }
<lambda>null380 override fun decodeShort(): Short = lexer.parseString("UShort") { toUShort().toShort() }
381 }
382
parseStringnull383 private inline fun <T> AbstractJsonLexer.parseString(expectedType: String, block: String.() -> T): T {
384 val input = consumeStringLenient()
385 try {
386 return input.block()
387 } catch (e: IllegalArgumentException) {
388 fail("Failed to parse type '$expectedType' for input '$input'")
389 }
390 }
391