diff options
24 files changed, 185 insertions, 160 deletions
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt index 654acd10..48e0f580 100644 --- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt +++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt @@ -21,7 +21,7 @@ class Cbor(val updateMode: UpdateMode = UpdateMode.BANNED, val encodeDefaults: B private open inner class CborListWriter(encoder: CborEncoder) : CborWriter(encoder) { override fun writeBeginToken() = encoder.startArray() - override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean = true + override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean = true } // Writes class as map [fieldName, fieldValue] @@ -46,8 +46,8 @@ class Cbor(val updateMode: UpdateMode = UpdateMode.BANNED, val encodeDefaults: B override fun endStructure(descriptor: SerialDescriptor) = encoder.end() - override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean { - val name = desc.getElementName(index) + override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean { + val name = descriptor.getElementName(index) encoder.encodeString(name) return true } @@ -176,8 +176,8 @@ class Cbor(val updateMode: UpdateMode = UpdateMode.BANNED, val encodeDefaults: B protected open fun skipBeginToken() = setSize(decoder.startMap()) - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { - val re = when (desc.kind) { + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + val re = when (descriptor.kind) { StructureKind.LIST, is PolymorphicKind -> CborListReader(decoder) StructureKind.MAP -> CborMapReader(decoder) else -> CborReader(decoder) diff --git a/formats/config/src/main/kotlin/org/jetbrains/kotlinx/serialization/config/ConfigReader.kt b/formats/config/src/main/kotlin/org/jetbrains/kotlinx/serialization/config/ConfigReader.kt index 8a0eb794..693341af 100644 --- a/formats/config/src/main/kotlin/org/jetbrains/kotlinx/serialization/config/ConfigReader.kt +++ b/formats/config/src/main/kotlin/org/jetbrains/kotlinx/serialization/config/ConfigReader.kt @@ -89,11 +89,11 @@ class ConfigParser(context: SerialModule = EmptyModule): AbstractSerialFormat(co return !conf.getIsNull(tag) } - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = when { - desc.kind.listLike -> ListConfigReader(conf.getList(currentTag)) - desc.kind.objLike -> if (ind > -1) ConfigReader(conf.getConfig(currentTag)) else this - desc.kind == StructureKind.MAP -> MapConfigReader(conf.getObject(currentTag)) + descriptor.kind.listLike -> ListConfigReader(conf.getList(currentTag)) + descriptor.kind.objLike -> if (ind > -1) ConfigReader(conf.getConfig(currentTag)) else this + descriptor.kind == StructureKind.MAP -> MapConfigReader(conf.getObject(currentTag)) else -> this } } @@ -101,11 +101,11 @@ class ConfigParser(context: SerialModule = EmptyModule): AbstractSerialFormat(co private inner class ListConfigReader(private val list: ConfigList) : ConfigConverter<Int>() { private var ind = -1 - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = when { - desc.kind.listLike -> ListConfigReader(list[currentTag] as ConfigList) - desc.kind.objLike -> ConfigReader((list[currentTag] as ConfigObject).toConfig()) - desc.kind == StructureKind.MAP -> MapConfigReader(list[currentTag] as ConfigObject) + descriptor.kind.listLike -> ListConfigReader(list[currentTag] as ConfigList) + descriptor.kind.objLike -> ConfigReader((list[currentTag] as ConfigObject).toConfig()) + descriptor.kind == StructureKind.MAP -> MapConfigReader(list[currentTag] as ConfigObject) else -> this } @@ -133,11 +133,11 @@ class ConfigParser(context: SerialModule = EmptyModule): AbstractSerialFormat(co private val indexSize = values.size * 2 - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = when { - desc.kind.listLike -> ListConfigReader(values[currentTag / 2] as ConfigList) - desc.kind.objLike -> ConfigReader((values[currentTag / 2] as ConfigObject).toConfig()) - desc.kind == StructureKind.MAP -> MapConfigReader(values[currentTag / 2] as ConfigObject) + descriptor.kind.listLike -> ListConfigReader(values[currentTag / 2] as ConfigList) + descriptor.kind.objLike -> ConfigReader((values[currentTag / 2] as ConfigObject).toConfig()) + descriptor.kind == StructureKind.MAP -> MapConfigReader(values[currentTag / 2] as ConfigObject) else -> this } diff --git a/formats/properties/commonMain/src/kotlinx/serialization/Properties.kt b/formats/properties/commonMain/src/kotlinx/serialization/Properties.kt index 2e9a2030..c60d28f7 100644 --- a/formats/properties/commonMain/src/kotlinx/serialization/Properties.kt +++ b/formats/properties/commonMain/src/kotlinx/serialization/Properties.kt @@ -93,11 +93,11 @@ public class Properties(context: SerialModule = EmptyModule) : AbstractSerialFor private var currentIndex = 0 - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { return InMapper(map).also { copyTagsTo(it) } } - override fun decodeCollectionSize(desc: SerialDescriptor): Int { + override fun decodeCollectionSize(descriptor: SerialDescriptor): Int { return decodeTaggedInt(nested("size")) } @@ -121,11 +121,11 @@ public class Properties(context: SerialModule = EmptyModule) : AbstractSerialFor private var currentIndex = 0 - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { return InNullableMapper(map).also { copyTagsTo(it) } } - override fun decodeCollectionSize(desc: SerialDescriptor): Int { + override fun decodeCollectionSize(descriptor: SerialDescriptor): Int { return decodeTaggedInt(nested("size")) } diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt index 03867447..f703e2b9 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt @@ -149,7 +149,7 @@ class ProtoBuf( val parentTag: ProtoDesc?, private val parentEncoder: ProtobufEncoder, private val stream: ByteArrayOutputStream = ByteArrayOutputStream() ) : ProtobufWriter(ProtobufEncoder(stream)) { - override fun endEncode(desc: SerialDescriptor) { + override fun endEncode(descriptor: SerialDescriptor) { if (parentTag != null) { parentEncoder.writeBytes(stream.toByteArray(), parentTag.first) } else { @@ -243,12 +243,17 @@ class ProtoBuf( ).first == serialId } ?: -1 - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = when (desc.kind) { - StructureKind.LIST -> RepeatedReader(decoder, currentTag) - StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> - ProtobufReader(makeDelimited(decoder, currentTagOrNull)) - StructureKind.MAP -> MapEntryReader(makeDelimited(decoder, currentTagOrNull), currentTagOrNull) - else -> throw SerializationException("Primitives are not supported at top-level") + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = + when (descriptor.kind) { + StructureKind.LIST -> RepeatedReader(decoder, currentTag) + StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> + ProtobufReader(makeDelimited(decoder, currentTagOrNull)) + StructureKind.MAP -> MapEntryReader(makeDelimited(decoder, currentTagOrNull), currentTagOrNull) + else -> throw SerializationException("Primitives are not supported at top-level") + } + + override fun endStructure(descriptor: SerialDescriptor) { + // Nothing } override fun decodeTaggedBoolean(tag: ProtoDesc): Boolean = when (val i = decoder.nextInt(ProtoNumberType.DEFAULT)) { diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt index c7bc1463..c5369b08 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt @@ -16,9 +16,9 @@ public annotation class ProtoType(val type: ProtoNumberType) internal typealias ProtoDesc = Pair<Int, ProtoNumberType> -internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefault: Boolean = false): ProtoDesc { - val idx = getProtoId(desc, index) ?: (if (zeroBasedDefault) index else index + 1) - val format = desc.findAnnotation<ProtoType>(index)?.type +internal fun extractParameters(descriptor: SerialDescriptor, index: Int, zeroBasedDefault: Boolean = false): ProtoDesc { + val idx = getProtoId(descriptor, index) ?: (if (zeroBasedDefault) index else index + 1) + val format = descriptor.findAnnotation<ProtoType>(index)?.type ?: ProtoNumberType.DEFAULT return idx to format } diff --git a/runtime/commonMain/src/kotlinx/serialization/ContextSerializer.kt b/runtime/commonMain/src/kotlinx/serialization/ContextSerializer.kt index 28b84159..f182aaa1 100644 --- a/runtime/commonMain/src/kotlinx/serialization/ContextSerializer.kt +++ b/runtime/commonMain/src/kotlinx/serialization/ContextSerializer.kt @@ -23,7 +23,7 @@ import kotlin.reflect.* */ @ImplicitReflectionSerializer public class ContextSerializer<T : Any>(private val serializableClass: KClass<T>) : KSerializer<T> { - public override val descriptor: SerialDescriptor = SerialDescriptor("kotlinx.serialization.ContextSerializer", UnionKind.CONTEXTUAL) {} + public override val descriptor: SerialDescriptor = SerialDescriptor("kotlinx.serialization.ContextSerializer", UnionKind.CONTEXTUAL) public override fun serialize(encoder: Encoder, value: T) { val serializer = encoder.context.getContextualOrDefault(value) diff --git a/runtime/commonMain/src/kotlinx/serialization/Decoding.kt b/runtime/commonMain/src/kotlinx/serialization/Decoding.kt index 9d01990a..796bccbc 100644 --- a/runtime/commonMain/src/kotlinx/serialization/Decoding.kt +++ b/runtime/commonMain/src/kotlinx/serialization/Decoding.kt @@ -8,19 +8,19 @@ import kotlinx.serialization.modules.* /** * Decoder is a core deserialization primitive that encapsulates the knowledge of the underlying - * format and its storage, exposing only structural methods to the deserializer, making it completely + * format and an underlying storage, exposing only structural methods to the deserializer, making it completely * format-agnostic. Deserialization process takes a decoder and asks him for a sequence of primitive elements, - * defined by a deserializer serial form, while decoder knows hot to retrieve these primitive elements from an actual format + * defined by a deserializer serial form, while decoder knows how to retrieve these primitive elements from an actual format * representations. * * Decoder provides high-level API that operates with basic primitive types, collections - * and nested structures. Internally, decoder represents input storage and operates with its state + * and nested structures. Internally, the decoder represents input storage, and operates with its state * and lower level format-specific details. * * To be more specific, serialization asks a decoder for a sequence of "give me an int, give me * a double, give me a list of strings and give me another object that is a nested int", while decoding * transforms this sequence into a format-specific commands such as "parse the part of the string until the next quotation mark - * as an int to retrieve an int, parse everything withing the next curly braces to retrieve elements of a nested object etc." + * as an int to retrieve an int, parse everything within the next curly braces to retrieve elements of a nested object etc." * * The symmetric interface for the serialization process is [Encoder]. * @@ -46,7 +46,7 @@ import kotlinx.serialization.modules.* * ``` * * E.g. if the decoder belongs to JSON format, then [beginStructure] will parse an opening bracket - * (`{` or `[`, depending on the descriptor kind), returning the [CompositeDecoder] that is aware of semicolon separator, + * (`{` or `[`, depending on the descriptor kind), returning the [CompositeDecoder] that is aware of colon separator, * that should be read after each key-value pair, whilst [CompositeDecoder.endStructure] will parse a closing bracket. * * ### Exception guarantees. @@ -78,7 +78,7 @@ import kotlinx.serialization.modules.* * * This deserializer does not know anything about the underlying data and will work with any properly-implemented decoder. * JSON, for example, parses an opening bracket `{` during the `beginStructure` call, checks that the next key - * after this bracket is `stringValue` (using the descriptor), returns the value after the semicolon as string value + * after this bracket is `stringValue` (using the descriptor), returns the value after the colon as string value * and parses closing bracket `}` during the `endStructure`. * XML would do the roughly the same, but with different separators and parsing structures, while ProtoBuf * machinery could be completely different. @@ -125,7 +125,7 @@ public interface Decoder { public fun decodeNotNullMark(): Boolean /** - * Parses the `null` value and returns it. + * Decodes the `null` value and returns it. */ public fun decodeNull(): Nothing? @@ -140,7 +140,7 @@ public interface Decoder { /** * Decodes a single byte value. - * Corresponding kind is [PrimitiveKind.BOOLEAN]. + * Corresponding kind is [PrimitiveKind.BYTE]. */ public fun decodeByte(): Byte @@ -152,18 +152,18 @@ public interface Decoder { /** * Decodes a 16-bit unicode character value. - * Corresponding kind is [PrimitiveKind.BOOLEAN]. + * Corresponding kind is [PrimitiveKind.CHAR]. */ public fun decodeChar(): Char /** - * Decodes a 32-bit int value + * Decodes a 32-bit integer value. * Corresponding kind is [PrimitiveKind.INT]. */ public fun decodeInt(): Int /** - * Decodes a 64-bit long value. + * Decodes a 64-bit integer value. * Corresponding kind is [PrimitiveKind.LONG]. */ public fun decodeLong(): Long @@ -194,7 +194,7 @@ public interface Decoder { * underlying input "C", [decodeEnum] method should return `2` as a result. * * This method does not imply any restrictions on the input format, - * the format is free to store the enum by its name, index, ordinal or any other + * the format is free to store the enum by its name, index, ordinal or any other enum representation. */ public fun decodeEnum(enumDescriptor: SerialDescriptor): Int @@ -214,7 +214,7 @@ public interface Decoder { * ``` * has three nested structures: the very beginning of the data, "b" value and "c" value. */ - public fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder + public fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder /** * Decodes the value of type [T] by delegating the decoding process to the given [deserializer]. @@ -258,10 +258,7 @@ public interface Decoder { * Please refer to [decodeElementIndex] for example of such loop. * * All `decode*` methods have `index` and `serialDescriptor` parameters with a strict semantics and constraints: - * * `descriptor` is always the same as one used in [Decoder.beginStructure]. While this parameter may seem redundant, - * it is required for efficient serialization process to avoid excessive field spilling. - * If you are writing your own format, you can safely ignore this parameter and use one used in `beginStructure` - * for simplicity. + * * `descriptor` argument is always the same as one used in [Decoder.beginStructure]. * * `index` of the element being decoded. For [sequential][decodeSequentially] decoding, it is always a monotonic * sequence from `0` to `descriptor.elementsCount` and for indexing-loop it is always an index that [decodeElementIndex] * has returned from the last call. @@ -309,9 +306,7 @@ public interface CompositeDecoder { * For example, composite decoder of JSON format will expect (and parse) * a closing bracket in the underlying input. */ - public fun endStructure(descriptor: SerialDescriptor) { - // TODO get rid of default implementation, method is too important - } + public fun endStructure(descriptor: SerialDescriptor) /** * Checks whether the current decoder supports strictly ordered decoding of the data @@ -403,8 +398,8 @@ public interface CompositeDecoder { * fun decodeElementIndex(descriptor: SerialDescriptor): Int { * // Ignore arrays * val nextKey: String? = myStringJsonParser.nextKey() - * if (nextObjectKey == null) return READ_DONE - * return descriptor.getElementIndex(nextKey) // getElementIndex can return UNKNOWN_FIELD + * if (nextKey == null) return READ_DONE + * return descriptor.getElementIndex(nextKey) // getElementIndex can return UNKNOWN_NAME * } * ``` */ @@ -454,10 +449,10 @@ public interface CompositeDecoder { * The resulting value is associated with the [descriptor] element at the given [index]. * The element at the given index should have [PrimitiveKind.INT] kind. */ - public fun decodeIntElement(desc: SerialDescriptor, index: Int): Int + public fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int /** - * Decodes a 32-bit long value from the underlying input. + * Decodes a 64-bit integer value from the underlying input. * The resulting value is associated with the [descriptor] element at the given [index]. * The element at the given index should have [PrimitiveKind.LONG] kind. */ @@ -473,7 +468,7 @@ public interface CompositeDecoder { /** * Decodes a 64-bit IEEE 754 floating point value from the underlying input. * The resulting value is associated with the [descriptor] element at the given [index]. - * The element at the given index should have [PrimitiveKind.LONG] kind. + * The element at the given index should have [PrimitiveKind.DOUBLE] kind. */ public fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double @@ -512,7 +507,7 @@ public interface CompositeDecoder { // Not documented public fun <T : Any> updateNullableSerializableElement( - desc: SerialDescriptor, + descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>, old: T? diff --git a/runtime/commonMain/src/kotlinx/serialization/ElementWise.kt b/runtime/commonMain/src/kotlinx/serialization/ElementWise.kt index b41b6137..57fe8bed 100644 --- a/runtime/commonMain/src/kotlinx/serialization/ElementWise.kt +++ b/runtime/commonMain/src/kotlinx/serialization/ElementWise.kt @@ -22,12 +22,6 @@ abstract class ElementValueEncoder : Encoder, CompositeEncoder { */ open fun encodeElement(desc: SerialDescriptor, index: Int): Boolean = true - /** - * Encodes that following value is not null. - * No-op by default. - */ - override fun encodeNotNullMark() {} - open fun encodeValue(value: Any): Unit = throw SerializationException("Non-serializable ${value::class} is not supported by ${this::class} encoder") @@ -52,8 +46,6 @@ abstract class ElementValueEncoder : Encoder, CompositeEncoder { override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = encodeValue(index) // Delegating implementation of CompositeEncoder - - final override fun encodeNonSerializableElement(descriptor: SerialDescriptor, index: Int, value: Any) { if (encodeElement(descriptor, index)) encodeValue(value) } final override fun encodeUnitElement(descriptor: SerialDescriptor, index: Int) { if (encodeElement(descriptor, index)) encodeUnit() } final override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) { if (encodeElement(descriptor, index)) encodeBoolean(value) } final override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) { if (encodeElement(descriptor, index)) encodeByte(value) } @@ -109,6 +101,9 @@ abstract class ElementValueDecoder : Decoder, CompositeDecoder { return this } + override fun endStructure(descriptor: SerialDescriptor) { + } + final override fun decodeUnitElement(desc: SerialDescriptor, index: Int) = decodeUnit() final override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = decodeBoolean() final override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte = decodeByte() diff --git a/runtime/commonMain/src/kotlinx/serialization/Encoding.kt b/runtime/commonMain/src/kotlinx/serialization/Encoding.kt index c33b6c69..979f28bd 100644 --- a/runtime/commonMain/src/kotlinx/serialization/Encoding.kt +++ b/runtime/commonMain/src/kotlinx/serialization/Encoding.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.modules.* * To be more specific, serialization transforms a value into a sequence of "here is an int, here is * a double, here a list of strings and here is another object that is a nested int", while encoding * transforms this sequence into a format-specific commands such as "insert opening curly bracket - * for a nested object start, insert a name of the value and the value separated with semicolon for an int etc." + * for a nested object start, insert a name of the value and the value separated with colon for an int etc." * * The symmetric interface for the deserialization process is [Decoder]. * @@ -45,7 +45,7 @@ import kotlinx.serialization.modules.* * ``` * * E.g. if the encoder belongs to JSON format, then [beginStructure] will write an opening bracket - * (`{` or `[`, depending on the descriptor kind), returning the [CompositeEncoder] that is aware of semicolon separator, + * (`{` or `[`, depending on the descriptor kind), returning the [CompositeEncoder] that is aware of colon separator, * that should be appended between each key-value pair, whilst [CompositeEncoder.endStructure] will write a closing bracket. * * ### Exception guarantees. @@ -118,7 +118,7 @@ public interface Encoder { * This method has a use in highly-performant binary formats and can * be safely ignore by most of the regular formats. */ - public fun encodeNotNullMark() + public fun encodeNotNullMark() {} /** * Encodes `null` value. @@ -136,7 +136,7 @@ public interface Encoder { /** * Encodes a single byte value. - * Corresponding kind is [PrimitiveKind.BOOLEAN]. + * Corresponding kind is [PrimitiveKind.BYTE]. */ public fun encodeByte(value: Byte) @@ -148,19 +148,19 @@ public interface Encoder { /** * Encodes a 16-bit unicode character value. - * Corresponding kind is [PrimitiveKind.BOOLEAN]. + * Corresponding kind is [PrimitiveKind.CHAR]. */ public fun encodeChar(value: Char) /** - * Encodes a 32-bit int value + * Encodes a 32-bit integer value. * Corresponding kind is [PrimitiveKind.INT]. */ public fun encodeInt(value: Int) /** - * Encodes a 64-bit int value - * Corresponding kind is [PrimitiveKind.INT]. + * Encodes a 64-bit integer value. + * Corresponding kind is [PrimitiveKind.LONG]. */ public fun encodeLong(value: Long) @@ -172,7 +172,7 @@ public interface Encoder { /** * Encodes a 64-bit IEEE 754 floating point value. - * Corresponding kind is [PrimitiveKind.FLOAT]. + * Corresponding kind is [PrimitiveKind.DOUBLE]. */ public fun encodeDouble(value: Double) @@ -221,7 +221,7 @@ public interface Encoder { * * // StringHolder serializer * fun serialize(encoder: Encoder, value: StringHolder) { - * val composite = encoder.beginStructure(descriptor) // One more '{' when the key "stringHolder" is already wriotten + * val composite = encoder.beginStructure(descriptor) // One more '{' when the key "stringHolder" is already written * composite.encodeStringElement(descriptor, 0, value.stringValue) // Serialize actual value * composite.endStructure(descriptor) // Closing bracket * } @@ -235,13 +235,13 @@ public interface Encoder { /** * Encodes the beginning of the collection with size [collectionSize] and the given serializer of its type parameters. + * This method has to be implemented only if you need to know collection size in advance, otherwise, [beginStructure] can be used. */ public fun beginCollection( descriptor: SerialDescriptor, collectionSize: Int, vararg typeSerializers: KSerializer<*> - ): CompositeEncoder = - beginStructure(descriptor, *typeSerializers) + ): CompositeEncoder = beginStructure(descriptor, *typeSerializers) /** * Encodes the [value] of type [T] by delegating the encoding process to the given [serializer]. @@ -271,7 +271,6 @@ public interface Encoder { * * All `encode*` methods have `index` and `serialDescriptor` parameters with a strict semantics and constraints: * * `descriptor` is always the same as one used in [Encoder.beginStructure]. While this parameter may seem redundant, - * * `descriptor` is always the same as one used in [Encoder.beginStructure]. While this parameter may seem redundant, * it is required for efficient serialization process to avoid excessive field spilling. * If you are writing your own format, you can safely ignore this parameter and use one used in `beginStructure` * for simplicity. @@ -292,9 +291,7 @@ public interface CompositeEncoder { * For example, composite encoder of JSON format will write * a closing bracket in the underlying input and reduce the number of nesting for pretty printing. */ - public fun endStructure(descriptor: SerialDescriptor) { - // TODO get rid of default implementation, method is too important - } + public fun endStructure(descriptor: SerialDescriptor) /** * Whether the format should encode values that are equal to the default values. @@ -390,8 +387,12 @@ public interface CompositeEncoder { value: T? ) - // No idea - public fun encodeNonSerializableElement(descriptor: SerialDescriptor, index: Int, value: Any) + @Deprecated( + level = DeprecationLevel.ERROR, + message = "This method is deprecated for removal. Please remove it from your implementation and delegate to default method instead" + ) + public fun encodeNonSerializableElement(descriptor: SerialDescriptor, index: Int, value: Any) { + } } /** diff --git a/runtime/commonMain/src/kotlinx/serialization/KSerializer.kt b/runtime/commonMain/src/kotlinx/serialization/KSerializer.kt index 6c4811b4..0f31bebe 100644 --- a/runtime/commonMain/src/kotlinx/serialization/KSerializer.kt +++ b/runtime/commonMain/src/kotlinx/serialization/KSerializer.kt @@ -11,14 +11,14 @@ package kotlinx.serialization * * Serialization is decoupled from the encoding process to make it completely format-agnostic. * Serialization represents a type as its serial form and is abstracted from the actual - * format (whether its JSON, ProtoBuf or a hashing) and is not aware of the underlying storage + * format (whether its JSON, ProtoBuf or a hashing) and unaware of the underlying storage * (whether it is a string builder, byte array or a network socket), while * encoding/decoding is abstracted from a particular type and its serial form and is responsible * for transforming primitives ("here in an int property 'foo'" call from a serializer) into a particular * format-specific representation ("for a given int, append a property name in quotation marks, - * then append a semicolon, then append an actual value" for JSON) and how to retrieve a primitive + * then append a colon, then append an actual value" for JSON) and how to retrieve a primitive * ("give me an int that is 'foo' property") from the underlying representation ("expect the next string to be 'foo', - * parse it, then parse semicolon, then parse a string until the next comma as an int an return it). + * parse it, then parse colon, then parse a string until the next comma as an int and return it). * * Serial form consists of a structural description, declared by the [descriptor] and * actual serialization and deserialization processes, defined by the corresponding @@ -54,9 +54,9 @@ public interface KSerializer<T> : SerializationStrategy<T>, DeserializationStrat * the shape of the serialized form (e.g. what elements are encoded as lists and what as primitives) * along with its metadata such as alternative names. * - * The descriptor is used dynamically, during serialization by encoders and decoders - * to introspect the type and metadata of [T]'s elements being encoded or decoded and - * statically, to introspect the type, infer the schema or to compare against the predefined schema. + * The descriptor is used during serialization by encoders and decoders + * to introspect the type and metadata of [T]'s elements being encoded or decoded, and + * to introspect the type, infer the schema or to compare against the predefined schema. */ override val descriptor: SerialDescriptor diff --git a/runtime/commonMain/src/kotlinx/serialization/SerialDescriptor.kt b/runtime/commonMain/src/kotlinx/serialization/SerialDescriptor.kt index ab1ae5af..5c517f27 100644 --- a/runtime/commonMain/src/kotlinx/serialization/SerialDescriptor.kt +++ b/runtime/commonMain/src/kotlinx/serialization/SerialDescriptor.kt @@ -71,12 +71,15 @@ package kotlinx.serialization * ) * * // Descriptor for such class: - * SerialDescriptor("my.package.Data", 3) { + * SerialDescriptor("my.package.Data") { * // intField is deliberately ignored by serializer -- not present in the descriptor as well * element<Long>("_longField") // longField is named as _longField * element("stringField", listDescriptor<String>()) * } * ``` + * + * For a classes that are represented as a single primitive value, + * [PrimitiveDescriptor] builder function can be used instead. */ public interface SerialDescriptor { /** diff --git a/runtime/commonMain/src/kotlinx/serialization/SerialDescriptorBuilder.kt b/runtime/commonMain/src/kotlinx/serialization/SerialDescriptorBuilder.kt index 7d40e369..cf290132 100644 --- a/runtime/commonMain/src/kotlinx/serialization/SerialDescriptorBuilder.kt +++ b/runtime/commonMain/src/kotlinx/serialization/SerialDescriptorBuilder.kt @@ -10,9 +10,6 @@ import kotlinx.serialization.internal.* * Builder for [SerialDescriptor]. * The resulting descriptor will be uniquely identified by the given [serialName], * with the corresponding [kind] and structure described in [builder] function. - * The count of descriptor elements should be known in advance to make API less error-prone, - * and this builder will throw [IllegalStateException] if count of added elements will be - * lesser or greater than [elementsCount]. * * Example: * ``` @@ -24,7 +21,7 @@ import kotlinx.serialization.internal.* * val nullableInt: Int? * ) * // Descriptor for such class: - * SerialDescriptor("my.package.Data", 3) { + * SerialDescriptor("my.package.Data") { * // intField is deliberately ignored by serializer -- not present in the descriptor as well * element<Long>("_longField") // longField is named as _longField * element("stringField", listDescriptor<String>()) @@ -36,6 +33,7 @@ public fun SerialDescriptor( kind: SerialKind = StructureKind.CLASS, builder: SerialDescriptorBuilder.() -> Unit = {} ): SerialDescriptor { + require(serialName.isNotBlank()) { "Blank serial names are prohibited" } val sdBuilder = SerialDescriptorBuilder(serialName) sdBuilder.builder() return SerialDescriptorImpl(serialName, kind, sdBuilder.elementNames.size, sdBuilder) @@ -49,7 +47,7 @@ public fun SerialDescriptor( * override val descriptor: SerialDescriptor = * PrimitiveDescriptor("kotlinx.serialization.LongAsStringSerializer", PrimitiveKind.STRING) * - * override fun serialize(encoder: Encoder, obj: Long) { + * override fun serialize(encoder: Encoder, value: Long) { * encoder.encodeString(obj.toString()) * } * @@ -59,7 +57,10 @@ public fun SerialDescriptor( * } * ``` */ -public fun PrimitiveDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor = PrimitiveDescriptorSafe(serialName, kind) +public fun PrimitiveDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor { + require(serialName.isNotBlank()) { "Blank serial names are prohibited" } + return PrimitiveDescriptorSafe(serialName, kind) +} /** * Returns new serial descriptor for the same type with [isNullable][SerialDescriptor.isNullable] @@ -112,7 +113,7 @@ public class SerialDescriptorBuilder internal constructor( * ) * * // Corresponding descriptor - * SerialDescriptor("package.Data", 2) { + * SerialDescriptor("package.Data") { * element<Int?>("intField", isOptional = true) * element<Long>("longField", annotations = listOf(protoIdAnnotationInstance)) * } @@ -124,9 +125,7 @@ public class SerialDescriptorBuilder internal constructor( annotations: List<Annotation> = emptyList(), isOptional: Boolean = false ) { - if (!uniqueNames.add(elementName)) { - error("Element with name '$elementName' is already registered") - } + require(uniqueNames.add(elementName)) { "Element with name '$elementName' is already registered" } elementNames += elementName elementDescriptors += descriptor elementAnnotations += annotations @@ -135,7 +134,7 @@ public class SerialDescriptorBuilder internal constructor( /** * Reified version of [element] function that - * extract descriptor using `serializer<T>().descriptor` call with the all the restrictions of `serializer<T>().descriptor`. + * extract descriptor using `serializer<T>().descriptor` call with all the restrictions of `serializer<T>().descriptor`. */ @ImplicitReflectionSerializer public inline fun <reified T> element( diff --git a/runtime/commonMain/src/kotlinx/serialization/SerialKinds.kt b/runtime/commonMain/src/kotlinx/serialization/SerialKinds.kt index 9301e83b..dc22dbbf 100644 --- a/runtime/commonMain/src/kotlinx/serialization/SerialKinds.kt +++ b/runtime/commonMain/src/kotlinx/serialization/SerialKinds.kt @@ -13,8 +13,8 @@ package kotlinx.serialization * depending on that, may write it as a plain value for primitive kinds, open a * curly brace '{' for class-like structures and square bracket '[' for list- and array- like structures. * - * Kinds are used both in runtime, to serialize a value properly and statically, - * to retrospect the type structure or build serialization schema. + * Kinds are used both during serialization, to serialize a value properly and statically, and + * to introspect the type structure or build serialization schema. * * Kind should match the structure of the serialized form, not the structure of the corresponding Kotlin class. * Meaning that if serializable class `class IntPair(val left: Int, val right: Int)` is represented by the serializer @@ -29,7 +29,7 @@ public sealed class SerialKind { } /** - * Values of primitive kinds that usually are represented as a single value. + * Values of primitive kinds usually are represented as a single value. * All default serializers for Kotlin [primitives types](https://kotlinlang.org/docs/tutorials/kotlin-for-py/primitive-data-types-and-their-limitations.html) * and [String] have primitive kind. * @@ -40,7 +40,7 @@ public sealed class SerialKind { * as a single [Int] value, a typical serializer will serialize its value in the following manner: * ``` * val intValue = color.rgbToInt() - * encoder.encodeInt() + * encoder.encodeInt(intValue) * ``` * and a corresponding [Decoder] counterpart. * @@ -131,7 +131,7 @@ public sealed class PrimitiveKind : SerialKind() { * * ### Lists * [LIST] represent a structure with potentially unknown in advance number of elements of the same type. - * All standard serializable [List] implementors are represented as [LIST] kind of the same type. + * All standard serializable [List] implementors and arrays are represented as [LIST] kind of the same type. * * ### Maps * [MAP] represent a structure with potentially unknown in advance number of key-value pairs of the same type. @@ -149,7 +149,7 @@ public sealed class PrimitiveKind : SerialKind() { * ``` * val composite = encoder.beginStructure(descriptor) // Denotes the start of the structure * composite.encodeIntElement(descriptor, index = 0, holder.myValue) - * composite.beginStructure(descriptor) // Denotes the end of the structure + * composite.endStructure(descriptor) // Denotes the end of the structure * ``` * and its corresponding [Decoder] counterpart. * @@ -168,7 +168,7 @@ public sealed class StructureKind : SerialKind() { public object CLASS : StructureKind() /** - * Structure kind for lists of an arbitrary length. + * Structure kind for lists and arrays of an arbitrary length. * Serializers typically encode classes with calls to [Encoder.beginCollection] and [CompositeEncoder.endStructure], * writing the elements of the list between these calls. * Built-in list serializers treat elements as homogeneous, though application-specific serializers may impose @@ -191,8 +191,8 @@ public sealed class StructureKind : SerialKind() { public object MAP : StructureKind() /** - * Structure kind for singleton objects defined with `object` keyword with. - * By default, objects are serialized as empty structures without any states and their identity is preserved + * Structure kind for singleton objects defined with `object` keyword. + * By default, objects are serialized as empty structures without any state and their identity is preserved * across serialization within the same process, so you always have the same instance of the object. * * Empty structure is represented as a call to [Encoder.beginStructure] with the following [CompositeEncoder.endStructure] @@ -211,9 +211,9 @@ public sealed class UnionKind : SerialKind() { /** * Represents a Kotlin [Enum] with statically known values. * All enum values should be enumerated in descriptor elements. - * Each element descriptor of a [Enum] kind represents an instance of a particular enum, - * and each [positional name][SerialDescriptor.getElementName] contains a corresponding - * enum element [name][Enum.name]. + * Each element descriptor of a [Enum] kind represents an instance of a particular enum + * and has an [StructureKind.OBJECT] kind. + * Each [positional name][SerialDescriptor.getElementName] contains a corresponding enum element [name][Enum.name]. * * Corresponding encoder and decoder methods are [Encoder.encodeEnum] and [Decoder.decodeEnum]. */ @@ -225,6 +225,15 @@ public sealed class UnionKind : SerialKind() { * be used for [contextual][ContextualSerialization] serialization. */ public object CONTEXTUAL : UnionKind() + + companion object { + @Deprecated( + "Moved out from UnionKind to StructureKind.", + ReplaceWith("StructureKind.OBJECT"), + DeprecationLevel.ERROR + ) + val OBJECT = StructureKind.OBJECT + } } /** diff --git a/runtime/commonMain/src/kotlinx/serialization/Tagged.kt b/runtime/commonMain/src/kotlinx/serialization/Tagged.kt index 928b05a6..5354ba6f 100644 --- a/runtime/commonMain/src/kotlinx/serialization/Tagged.kt +++ b/runtime/commonMain/src/kotlinx/serialization/Tagged.kt @@ -85,9 +85,7 @@ abstract class TaggedEncoder<Tag : Any?> : Encoder, CompositeEncoder { /** * Format-specific replacement for [endStructure], because latter is overridden to manipulate tag stack. */ - open fun endEncode(desc: SerialDescriptor) {} - - final override fun encodeNonSerializableElement(descriptor: SerialDescriptor, index: Int, value: Any) = encodeTaggedValue(descriptor.getTag(index), value) + open fun endEncode(descriptor: SerialDescriptor) {} final override fun encodeUnitElement(descriptor: SerialDescriptor, index: Int) = encodeTaggedUnit(descriptor.getTag(index)) final override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) = encodeTaggedBoolean(descriptor.getTag(index), value) @@ -100,13 +98,13 @@ abstract class TaggedEncoder<Tag : Any?> : Encoder, CompositeEncoder { final override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) = encodeTaggedChar(descriptor.getTag(index), value) final override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String) = encodeTaggedString(descriptor.getTag(index), value) - final override fun <T : Any?> encodeSerializableElement(desc: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T) { - if (encodeElement(desc, index)) + final override fun <T : Any?> encodeSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T) { + if (encodeElement(descriptor, index)) encodeSerializableValue(serializer, value) } - final override fun <T : Any> encodeNullableSerializableElement(desc: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T?) { - if (encodeElement(desc, index)) + final override fun <T : Any> encodeNullableSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T?) { + if (encodeElement(descriptor, index)) encodeNullableSerializableValue(serializer, value) } @@ -182,32 +180,36 @@ abstract class TaggedDecoder<Tag : Any?> : Decoder, CompositeDecoder { final override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeTaggedEnum(popTag(), enumDescriptor) - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { return this } - final override fun decodeUnitElement(desc: SerialDescriptor, index: Int) = decodeTaggedUnit(desc.getTag(index)) + override fun endStructure(descriptor: SerialDescriptor) { + // Nothing + } + + final override fun decodeUnitElement(descriptor: SerialDescriptor, index: Int) = decodeTaggedUnit(descriptor.getTag(index)) final override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = decodeTaggedBoolean(descriptor.getTag(index)) final override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte = decodeTaggedByte(descriptor.getTag(index)) final override fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short = decodeTaggedShort(descriptor.getTag(index)) - final override fun decodeIntElement(desc: SerialDescriptor, index: Int): Int = decodeTaggedInt(desc.getTag(index)) + final override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int = decodeTaggedInt(descriptor.getTag(index)) final override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long = decodeTaggedLong(descriptor.getTag(index)) final override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float = decodeTaggedFloat(descriptor.getTag(index)) - final override fun decodeDoubleElement(desc: SerialDescriptor, index: Int): Double = decodeTaggedDouble(desc.getTag(index)) + final override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double = decodeTaggedDouble(descriptor.getTag(index)) final override fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char = decodeTaggedChar(descriptor.getTag(index)) final override fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String = decodeTaggedString(descriptor.getTag(index)) - final override fun <T : Any?> decodeSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>): T = - tagBlock(desc.getTag(index)) { decodeSerializableValue(deserializer) } + final override fun <T : Any?> decodeSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>): T = + tagBlock(descriptor.getTag(index)) { decodeSerializableValue(deserializer) } - final override fun <T : Any> decodeNullableSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>): T? = - tagBlock(desc.getTag(index)) { decodeNullableSerializableValue(deserializer) } + final override fun <T : Any> decodeNullableSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>): T? = + tagBlock(descriptor.getTag(index)) { decodeNullableSerializableValue(deserializer) } - override fun <T> updateSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>, old: T): T = - tagBlock(desc.getTag(index)) { updateSerializableValue(deserializer, old) } + override fun <T> updateSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>, old: T): T = + tagBlock(descriptor.getTag(index)) { updateSerializableValue(deserializer, old) } - override fun <T : Any> updateNullableSerializableElement(desc: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>, old: T?): T? = - tagBlock(desc.getTag(index)) { updateNullableSerializableValue(deserializer, old) } + override fun <T : Any> updateNullableSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T?>, old: T?): T? = + tagBlock(descriptor.getTag(index)) { updateNullableSerializableValue(deserializer, old) } private fun <E> tagBlock(tag: Tag, block: () -> E): E { pushTag(tag) diff --git a/runtime/commonMain/src/kotlinx/serialization/internal/Arrays.common.kt b/runtime/commonMain/src/kotlinx/serialization/internal/Arrays.common.kt index 821af6dc..a06b2d29 100644 --- a/runtime/commonMain/src/kotlinx/serialization/internal/Arrays.common.kt +++ b/runtime/commonMain/src/kotlinx/serialization/internal/Arrays.common.kt @@ -4,6 +4,12 @@ package kotlinx.serialization.internal -// Array.get that checks indices on JS +/** + * Array.get that checks indices on JS + */ internal expect fun <T> Array<T>.getChecked(index: Int): T + +/** + * Array.get that checks indices on JS + */ internal expect fun BooleanArray.getChecked(index: Int): Boolean diff --git a/runtime/commonMain/src/kotlinx/serialization/internal/Enums.kt b/runtime/commonMain/src/kotlinx/serialization/internal/Enums.kt index 03eeff4a..9a682e82 100644 --- a/runtime/commonMain/src/kotlinx/serialization/internal/Enums.kt +++ b/runtime/commonMain/src/kotlinx/serialization/internal/Enums.kt @@ -47,7 +47,8 @@ public class EnumDescriptor( // Used for enums that are not explicitly serializable @InternalSerializationApi -@Deprecated(level = DeprecationLevel.HIDDEN, message = "For plugin-generated code") +@Deprecated(level = DeprecationLevel.ERROR, message = "For plugin-generated code, " + + "should not be used directly. For the custom serializers please report your use-case to project issues, so proper public API could be introduced instead") public class EnumSerializer<T : Enum<T>>( serialName: String, private val values: Array<T> diff --git a/runtime/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt b/runtime/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt index 77f87c38..ff714641 100644 --- a/runtime/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt +++ b/runtime/commonMain/src/kotlinx/serialization/internal/ObjectSerializer.kt @@ -12,7 +12,11 @@ import kotlinx.serialization.* * uses an [object instance][objectInstance]. * By default, a singleton is serialized as an empty structure, e.g. `{}` in JSON */ -@Deprecated("", level = DeprecationLevel.HIDDEN) +@Deprecated( + "For plugin-generated code, " + + "should not be used directly. For the custom serializers please report your use-case to project issues, so proper public API could be introduced instead", + level = DeprecationLevel.ERROR +) public class ObjectSerializer<T : Any>(serialName: String, private val objectInstance: T) : KSerializer<T> { override val descriptor: SerialDescriptor = SerialDescriptor(serialName, StructureKind.OBJECT) diff --git a/runtime/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt b/runtime/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt index 7937ccf6..812dc0cb 100644 --- a/runtime/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt +++ b/runtime/commonMain/src/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.kt @@ -27,8 +27,8 @@ public open class PluginGeneratedSerialDescriptor( // Classes rarely have annotations, so we can save up a bit of allocations here private var classAnnotations: MutableList<Annotation>? = null private var flags = BooleanArray(elementsCount) + internal val namesSet: Set<String> get() = indices.keys // don't change lazy mode: KT-32871, KT-32872 - internal val namesSet: Set<String> by lazy { names.toHashSet() } private val indices: Map<String, Int> by lazy { buildIndices() } public fun addElement(name: String, isOptional: Boolean = false) { @@ -59,7 +59,7 @@ public open class PluginGeneratedSerialDescriptor( override fun getElementDescriptor(index: Int): SerialDescriptor { return generatedSerializer?.childSerializers()?.get(index)?.descriptor - ?: error("Unexpected call to getElementDescriptor($index)") + ?: throw IndexOutOfBoundsException("$serialName descriptor has only $elementsCount elements, index: $index") } override fun isElementOptional(index: Int): Boolean = flags.getChecked(index) diff --git a/runtime/commonMain/src/kotlinx/serialization/internal/Primitives.kt b/runtime/commonMain/src/kotlinx/serialization/internal/Primitives.kt index 27508fc8..fa0f74e7 100644 --- a/runtime/commonMain/src/kotlinx/serialization/internal/Primitives.kt +++ b/runtime/commonMain/src/kotlinx/serialization/internal/Primitives.kt @@ -69,9 +69,8 @@ private fun checkName(serialName: String) { } @Suppress("UNCHECKED_CAST") -internal fun <T : Any> KClass<T>.builtinSerializerOrNull(): KSerializer<T>? { - return BUILTIN_SERIALIZERS[this] as? KSerializer<T> -} +internal fun <T : Any> KClass<T>.builtinSerializerOrNull(): KSerializer<T>? = + BUILTIN_SERIALIZERS[this] as KSerializer<T>? @Deprecated(level = DeprecationLevel.HIDDEN, message = "Binary compatibility") object UnitSerializer : KSerializer<Unit> { diff --git a/runtime/commonMain/src/kotlinx/serialization/json/JsonElementSerializer.kt b/runtime/commonMain/src/kotlinx/serialization/json/JsonElementSerializer.kt index d12ab702..0fa4bae9 100644 --- a/runtime/commonMain/src/kotlinx/serialization/json/JsonElementSerializer.kt +++ b/runtime/commonMain/src/kotlinx/serialization/json/JsonElementSerializer.kt @@ -54,7 +54,7 @@ public object JsonElementSerializer : KSerializer<JsonElement> { @Serializer(forClass = JsonPrimitive::class) public object JsonPrimitiveSerializer : KSerializer<JsonPrimitive> { override val descriptor: SerialDescriptor = - SerialDescriptor("kotlinx.serialization.json.JsonPrimitive", PrimitiveKind.STRING) {} + SerialDescriptor("kotlinx.serialization.json.JsonPrimitive", PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: JsonPrimitive) { verify(encoder) @@ -80,7 +80,7 @@ public object JsonPrimitiveSerializer : KSerializer<JsonPrimitive> { public object JsonNullSerializer : KSerializer<JsonNull> { // technically, JsonNull is an object, but it does not call beginStructure/endStructure at all override val descriptor: SerialDescriptor = - SerialDescriptor("kotlinx.serialization.json.JsonNull", UnionKind.ENUM_KIND) {} + SerialDescriptor("kotlinx.serialization.json.JsonNull", UnionKind.ENUM_KIND) override fun serialize(encoder: Encoder, value: JsonNull) { verify(encoder) @@ -102,7 +102,7 @@ public object JsonNullSerializer : KSerializer<JsonNull> { public object JsonLiteralSerializer : KSerializer<JsonLiteral> { override val descriptor: SerialDescriptor = - SerialDescriptor("kotlinx.serialization.json.JsonLiteral", PrimitiveKind.STRING) {} + PrimitiveDescriptor("kotlinx.serialization.json.JsonLiteral", PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: JsonLiteral) { verify(encoder) diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt index a70890d6..88e2749a 100644 --- a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt +++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt @@ -7,6 +7,7 @@ package kotlinx.serialization.json.internal import kotlinx.serialization.* +import kotlinx.serialization.internal.* import kotlinx.serialization.json.* import kotlinx.serialization.modules.* import kotlin.jvm.* @@ -45,12 +46,12 @@ private sealed class AbstractJsonTreeInput( override fun composeName(parentName: String, childName: String): String = childName - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { val currentObject = currentObject() - return when (desc.kind) { + return when (descriptor.kind) { StructureKind.LIST, is PolymorphicKind -> JsonTreeListInput(json, cast(currentObject)) StructureKind.MAP -> json.selectMapMode( - desc, + descriptor, { JsonTreeMapInput(json, cast(currentObject)) }, { JsonTreeListInput(json, cast(currentObject)) } ) @@ -58,6 +59,10 @@ private sealed class AbstractJsonTreeInput( } } + override fun endStructure(descriptor: SerialDescriptor) { + // Nothing + } + protected open fun getValue(tag: String): JsonPrimitive { val currentElement = currentElement(tag) return currentElement as? JsonPrimitive ?: throw JsonDecodingException( @@ -124,14 +129,10 @@ private open class JsonTreeInput(json: Json, override val obj: JsonObject) : Abs override fun currentElement(tag: String): JsonElement = obj.getValue(tag) override fun endStructure(descriptor: SerialDescriptor) { - if (!configuration.strictMode || descriptor.kind is PolymorphicKind.OPEN) return + if (!configuration.strictMode || descriptor.kind is PolymorphicKind) return // Validate keys - val names = HashSet<String>(descriptor.elementsCount) - for (i in 0 until descriptor.elementsCount) { - names += descriptor.getElementName(i) - } - + val names = descriptor.cachedSerialNames() for (key in obj.keys) { if (key !in names) throw jsonUnknownKeyException(-1, key) } diff --git a/runtime/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt b/runtime/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt index bdbaf38d..e6ee096e 100644 --- a/runtime/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt +++ b/runtime/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt @@ -4,11 +4,9 @@ package kotlinx.serialization -import kotlinx.serialization.internal.* import kotlinx.serialization.test.* import kotlin.test.* -@ImplicitReflectionSerializer class SerialDescriptorBuilderTest { @Serializable @@ -72,11 +70,18 @@ class SerialDescriptorBuilderTest { @Test fun testMisconfiguration() { - assertFailsWith<IllegalStateException> { - SerialDescriptor("", StructureKind.CLASS) { + assertFailsWith<IllegalArgumentException> { + SerialDescriptor("a", StructureKind.CLASS) { element<Int>("i") element<Int>("i") } } + + assertFailsWith<IllegalArgumentException> { SerialDescriptor("", StructureKind.CLASS) } + assertFailsWith<IllegalArgumentException> { SerialDescriptor("\t", StructureKind.CLASS) } + assertFailsWith<IllegalArgumentException> { SerialDescriptor(" ", StructureKind.CLASS) } + assertFailsWith<IllegalArgumentException> { PrimitiveDescriptor("", PrimitiveKind.STRING) } + assertFailsWith<IllegalArgumentException> { PrimitiveDescriptor(" ", PrimitiveKind.STRING) } + assertFailsWith<IllegalArgumentException> { PrimitiveDescriptor("\t", PrimitiveKind.STRING) } } } diff --git a/runtime/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt b/runtime/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt index 6453d174..5bcbadca 100644 --- a/runtime/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt +++ b/runtime/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt @@ -32,8 +32,8 @@ class CheckedData<T : Any>(val data: T, val checkSum: ByteArray) { @Serializer(forClass = CheckedData::class) class CheckedDataSerializer<T : Any>(private val dataSerializer: KSerializer<T>) : KSerializer<CheckedData<T>> { override val descriptor: SerialDescriptor = SerialDescriptor("CheckedDataSerializer") { - val typeDescriptor = dataSerializer.descriptor - element("data", typeDescriptor) + val dataDescriptor = dataSerializer.descriptor + element("data", dataDescriptor) element("checkSum", ByteArraySerializer.descriptor) } diff --git a/runtime/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt b/runtime/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt index 8307633c..4eaee806 100644 --- a/runtime/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt +++ b/runtime/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt @@ -51,6 +51,6 @@ class SerialNameCollisionInSealedClassesTest { BaseCollision.Child() BaseCollision.ChildCollided() BaseCollision.ChildCollided.serializer().descriptor // Doesn't fail - assertFailsWith<IllegalStateException> { BaseCollision.serializer().descriptor } + assertFailsWith<IllegalArgumentException> { BaseCollision.serializer().descriptor } } } |