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