• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017-2022 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.builtins.*
9 import kotlinx.serialization.descriptors.*
10 import kotlinx.serialization.encoding.*
11 import kotlinx.serialization.json.*
12 import kotlinx.serialization.modules.*
13 
14 private val unsignedNumberDescriptors = setOf(
15     UInt.serializer().descriptor,
16     ULong.serializer().descriptor,
17     UByte.serializer().descriptor,
18     UShort.serializer().descriptor
19 )
20 
21 internal val SerialDescriptor.isUnsignedNumber: Boolean
22     get() = this.isInline && this in unsignedNumberDescriptors
23 
24 internal val SerialDescriptor.isUnquotedLiteral: Boolean
25     get() = this.isInline && this == jsonUnquotedLiteralDescriptor
26 
27 @OptIn(ExperimentalSerializationApi::class)
28 internal class StreamingJsonEncoder(
29     private val composer: Composer,
30     override val json: Json,
31     private val mode: WriteMode,
32     private val modeReuseCache: Array<JsonEncoder?>?
33 ) : JsonEncoder, AbstractEncoder() {
34 
35     internal constructor(
36         output: InternalJsonWriter, json: Json, mode: WriteMode,
37         modeReuseCache: Array<JsonEncoder?>
38     ) : this(Composer(output, json), json, mode, modeReuseCache)
39 
40     override val serializersModule: SerializersModule = json.serializersModule
41     private val configuration = json.configuration
42 
43     // Forces serializer to wrap all values into quotes
44     private var forceQuoting: Boolean = false
45     private var polymorphicDiscriminator: String? = null
46 
47     init {
48         val i = mode.ordinal
49         if (modeReuseCache != null) {
50             if (modeReuseCache[i] !== null || modeReuseCache[i] !== this)
51                 modeReuseCache[i] = this
52         }
53     }
54 
encodeJsonElementnull55     override fun encodeJsonElement(element: JsonElement) {
56         encodeSerializableValue(JsonElementSerializer, element)
57     }
58 
shouldEncodeElementDefaultnull59     override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean {
60         return configuration.encodeDefaults
61     }
62 
encodeSerializableValuenull63     override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
64         encodePolymorphically(serializer, value) {
65             polymorphicDiscriminator = it
66         }
67     }
68 
encodeTypeInfonull69     private fun encodeTypeInfo(descriptor: SerialDescriptor) {
70         composer.nextItem()
71         encodeString(polymorphicDiscriminator!!)
72         composer.print(COLON)
73         composer.space()
74         encodeString(descriptor.serialName)
75     }
76 
beginStructurenull77     override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
78         val newMode = json.switchMode(descriptor)
79         if (newMode.begin != INVALID) { // entry
80             composer.print(newMode.begin)
81             composer.indent()
82         }
83 
84         if (polymorphicDiscriminator != null) {
85             encodeTypeInfo(descriptor)
86             polymorphicDiscriminator = null
87         }
88 
89         if (mode == newMode) {
90             return this
91         }
92 
93         return modeReuseCache?.get(newMode.ordinal) ?: StreamingJsonEncoder(composer, json, newMode, modeReuseCache)
94     }
95 
endStructurenull96     override fun endStructure(descriptor: SerialDescriptor) {
97         if (mode.end != INVALID) {
98             composer.unIndent()
99             composer.nextItemIfNotFirst()
100             composer.print(mode.end)
101         }
102     }
103 
encodeElementnull104     override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
105         when (mode) {
106             WriteMode.LIST -> {
107                 if (!composer.writingFirst)
108                     composer.print(COMMA)
109                 composer.nextItem()
110             }
111             WriteMode.MAP -> {
112                 if (!composer.writingFirst) {
113                     forceQuoting = if (index % 2 == 0) {
114                         composer.print(COMMA)
115                         composer.nextItem() // indent should only be put after commas in map
116                         true
117                     } else {
118                         composer.print(COLON)
119                         composer.space()
120                         false
121                     }
122                 } else {
123                     forceQuoting = true
124                     composer.nextItem()
125                 }
126             }
127             WriteMode.POLY_OBJ -> {
128                 if (index == 0)
129                     forceQuoting = true
130                 if (index == 1) {
131                     composer.print(COMMA)
132                     composer.space()
133                     forceQuoting = false
134                 }
135             }
136             else -> {
137                 if (!composer.writingFirst)
138                     composer.print(COMMA)
139                 composer.nextItem()
140                 encodeString(descriptor.getJsonElementName(json, index))
141                 composer.print(COLON)
142                 composer.space()
143             }
144         }
145         return true
146     }
147 
encodeNullableSerializableElementnull148     override fun <T : Any> encodeNullableSerializableElement(
149         descriptor: SerialDescriptor,
150         index: Int,
151         serializer: SerializationStrategy<T>,
152         value: T?
153     ) {
154         if (value != null || configuration.explicitNulls) {
155             super.encodeNullableSerializableElement(descriptor, index, serializer, value)
156         }
157     }
158 
encodeInlinenull159     override fun encodeInline(descriptor: SerialDescriptor): Encoder =
160         when {
161             descriptor.isUnsignedNumber -> StreamingJsonEncoder(composerAs(::ComposerForUnsignedNumbers), json, mode, null)
162             descriptor.isUnquotedLiteral -> StreamingJsonEncoder(composerAs(::ComposerForUnquotedLiterals), json, mode, null)
163             else                        -> super.encodeInline(descriptor)
164         }
165 
composerAsnull166     private inline fun <reified T: Composer> composerAs(composerCreator: (writer: InternalJsonWriter, forceQuoting: Boolean) -> T): T {
167         // If we're inside encodeInline().encodeSerializableValue, we should preserve the forceQuoting state
168         // inside the composer, but not in the encoder (otherwise we'll get into `if (forceQuoting) encodeString(value.toString())` part
169         // and unsigned numbers would be encoded incorrectly)
170         return if (composer is T) composer
171         else composerCreator(composer.writer, forceQuoting)
172     }
173 
encodeNullnull174     override fun encodeNull() {
175         composer.print(NULL)
176     }
177 
encodeBooleannull178     override fun encodeBoolean(value: Boolean) {
179         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
180     }
181 
encodeBytenull182     override fun encodeByte(value: Byte) {
183         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
184     }
185 
encodeShortnull186     override fun encodeShort(value: Short) {
187         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
188     }
189 
encodeIntnull190     override fun encodeInt(value: Int) {
191         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
192     }
193 
encodeLongnull194     override fun encodeLong(value: Long) {
195         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
196     }
197 
encodeFloatnull198     override fun encodeFloat(value: Float) {
199         // First encode value, then check, to have a prettier error message
200         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
201         if (!configuration.allowSpecialFloatingPointValues && !value.isFinite()) {
202             throw InvalidFloatingPointEncoded(value, composer.writer.toString())
203         }
204     }
205 
encodeDoublenull206     override fun encodeDouble(value: Double) {
207         // First encode value, then check, to have a prettier error message
208         if (forceQuoting) encodeString(value.toString()) else composer.print(value)
209         if (!configuration.allowSpecialFloatingPointValues && !value.isFinite()) {
210             throw InvalidFloatingPointEncoded(value, composer.writer.toString())
211         }
212     }
213 
encodeCharnull214     override fun encodeChar(value: Char) {
215         encodeString(value.toString())
216     }
217 
encodeStringnull218     override fun encodeString(value: String) = composer.printQuoted(value)
219 
220     override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
221         encodeString(enumDescriptor.getElementName(index))
222     }
223 }
224