1 /* 2 * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.serialization 6 7 import kotlinx.serialization.encoding.* 8 import kotlinx.serialization.descriptors.* 9 10 /** 11 * A generic exception indicating the problem in serialization or deserialization process. 12 * 13 * This is a generic exception type that can be thrown during problems at any stage of the serialization, 14 * including encoding, decoding, serialization, deserialization, and validation. 15 * [SerialFormat] implementors should throw subclasses of this exception at any unexpected event, 16 * whether it is a malformed input or unsupported class layout. 17 * 18 * [SerializationException] is a subclass of [IllegalArgumentException] for the sake of consistency and user-defined validation: 19 * Any serialization exception is triggered by the illegal input, whether 20 * it is a serializer that does not support specific structure or an invalid input. 21 * 22 * It is also an established pattern to validate input in user's classes in the following manner: 23 * ``` 24 * @Serializable 25 * class Foo(...) { 26 * init { 27 * required(age > 0) { ... } 28 * require(name.isNotBlank()) { ... } 29 * } 30 * } 31 * ``` 32 * While clearly being serialization error (when compromised data was deserialized), 33 * Kotlin way is to throw `IllegalArgumentException` here instead of using library-specific `SerializationException`. 34 * 35 * For general "catch-all" patterns around deserialization of potentially 36 * untrusted/invalid/corrupted data it is recommended to catch `IllegalArgumentException` type 37 * to avoid catching irrelevant to serialization errors such as `OutOfMemoryError` or domain-specific ones. 38 */ 39 public open class SerializationException : IllegalArgumentException { 40 41 /** 42 * Creates an instance of [SerializationException] without any details. 43 */ 44 public constructor() 45 46 /** 47 * Creates an instance of [SerializationException] with the specified detail [message]. 48 */ 49 public constructor(message: String?) : super(message) 50 51 /** 52 * Creates an instance of [SerializationException] with the specified detail [message], and the given [cause]. 53 */ 54 public constructor(message: String?, cause: Throwable?) : super(message, cause) 55 56 /** 57 * Creates an instance of [SerializationException] with the specified [cause]. 58 */ 59 public constructor(cause: Throwable?) : super(cause) 60 } 61 62 /** 63 * Thrown when [KSerializer] did not receive a non-optional property from [CompositeDecoder] and [CompositeDecoder.decodeElementIndex] 64 * had already returned [CompositeDecoder.DECODE_DONE]. 65 * 66 * [MissingFieldException] is thrown on missing field from all [auto-generated][Serializable] serializers and it 67 * is recommended to throw this exception from user-defined serializers. 68 * 69 * [MissingFieldException] is constructed from the following properties: 70 * - [missingFields] -- fields that were required for the deserialization but have not been found. 71 * They are always non-empty and their names match the corresponding names in [SerialDescriptor.elementNames] 72 * - Optional `serialName` -- serial name of the enclosing class that failed to get deserialized. 73 * Matches the corresponding [SerialDescriptor.serialName]. 74 * 75 * @see SerializationException 76 * @see KSerializer 77 */ 78 @ExperimentalSerializationApi 79 public class MissingFieldException( 80 missingFields: List<String>, message: String?, cause: Throwable? 81 ) : SerializationException(message, cause) { 82 83 /** 84 * List of fields that were required but not found during deserialization. 85 * Contains at least one element. 86 */ 87 public val missingFields: List<String> = missingFields 88 89 /** 90 * Creates an instance of [MissingFieldException] for the given [missingFields] and [serialName] of 91 * the corresponding serializer. 92 */ 93 public constructor( 94 missingFields: List<String>, 95 serialName: String 96 ) : this( 97 missingFields, 98 if (missingFields.size == 1) "Field '${missingFields[0]}' is required for type with serial name '$serialName', but it was missing" 99 else "Fields $missingFields are required for type with serial name '$serialName', but they were missing", 100 null 101 ) 102 103 /** 104 * Creates an instance of [MissingFieldException] for the given [missingField] and [serialName] of 105 * the corresponding serializer. 106 */ 107 public constructor( 108 missingField: String, 109 serialName: String 110 ) : this( 111 listOf(missingField), 112 "Field '$missingField' is required for type with serial name '$serialName', but it was missing", 113 null 114 ) 115 116 @PublishedApi // Constructor used by the generated serializers 117 internal constructor(missingField: String) : this( 118 listOf(missingField), 119 "Field '$missingField' is required, but it was missing", 120 null 121 ) 122 } 123 124 /** 125 * Thrown when [KSerializer] received unknown property index from [CompositeDecoder.decodeElementIndex]. 126 * 127 * This exception means that data schema has changed in backwards-incompatible way. 128 */ 129 @PublishedApi 130 internal class UnknownFieldException 131 // This constructor is used by coroutines exception recovery 132 internal constructor(message: String?) : SerializationException(message) { 133 // This constructor is used by the generated serializers 134 constructor(index: Int) : this("An unknown field for index $index") 135 } 136