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 User(val age: Int, val name: String) { 26 * init { 27 * require(age > 0) { ... } 28 * require(name.isNotBlank()) { ... } 29 * } 30 * } 31 * 32 * Json.decodeFromString<User>("""{"age": -100, "name": ""}""") // throws IllegalArgumentException from require() 33 * ``` 34 * While clearly being serialization error (when compromised data was deserialized), 35 * Kotlin way is to throw `IllegalArgumentException` here instead of using library-specific `SerializationException`. 36 * 37 * For general "catch-all" patterns around deserialization of potentially 38 * untrusted/invalid/corrupted data it is recommended to catch `IllegalArgumentException` type 39 * to avoid catching irrelevant to serialization errors such as `OutOfMemoryError` or domain-specific ones. 40 */ 41 public open class SerializationException : IllegalArgumentException { 42 43 /** 44 * Creates an instance of [SerializationException] without any details. 45 */ 46 public constructor() 47 48 /** 49 * Creates an instance of [SerializationException] with the specified detail [message]. 50 */ 51 public constructor(message: String?) : super(message) 52 53 /** 54 * Creates an instance of [SerializationException] with the specified detail [message], and the given [cause]. 55 */ 56 public constructor(message: String?, cause: Throwable?) : super(message, cause) 57 58 /** 59 * Creates an instance of [SerializationException] with the specified [cause]. 60 */ 61 public constructor(cause: Throwable?) : super(cause) 62 } 63 64 /** 65 * Thrown when [KSerializer] did not receive a non-optional property from [CompositeDecoder] and [CompositeDecoder.decodeElementIndex] 66 * had already returned [CompositeDecoder.DECODE_DONE]. 67 * 68 * [MissingFieldException] is thrown on missing field from all [auto-generated][Serializable] serializers and it 69 * is recommended to throw this exception from user-defined serializers. 70 * 71 * [MissingFieldException] is constructed from the following properties: 72 * - [missingFields] -- fields that were required for the deserialization but have not been found. 73 * They are always non-empty and their names match the corresponding names in [SerialDescriptor.elementNames] 74 * - Optional `serialName` -- serial name of the enclosing class that failed to get deserialized. 75 * Matches the corresponding [SerialDescriptor.serialName]. 76 * 77 * @see SerializationException 78 * @see KSerializer 79 */ 80 @ExperimentalSerializationApi 81 public class MissingFieldException( 82 missingFields: List<String>, message: String?, cause: Throwable? 83 ) : SerializationException(message, cause) { 84 85 /** 86 * List of fields that were required but not found during deserialization. 87 * Contains at least one element. 88 */ 89 public val missingFields: List<String> = missingFields 90 91 /** 92 * Creates an instance of [MissingFieldException] for the given [missingFields] and [serialName] of 93 * the corresponding serializer. 94 */ 95 public constructor( 96 missingFields: List<String>, 97 serialName: String 98 ) : this( 99 missingFields, 100 if (missingFields.size == 1) "Field '${missingFields[0]}' is required for type with serial name '$serialName', but it was missing" 101 else "Fields $missingFields are required for type with serial name '$serialName', but they were missing", 102 null 103 ) 104 105 /** 106 * Creates an instance of [MissingFieldException] for the given [missingField] and [serialName] of 107 * the corresponding serializer. 108 */ 109 public constructor( 110 missingField: String, 111 serialName: String 112 ) : this( 113 listOf(missingField), 114 "Field '$missingField' is required for type with serial name '$serialName', but it was missing", 115 null 116 ) 117 118 @PublishedApi // Constructor used by the generated serializers 119 internal constructor(missingField: String) : this( 120 listOf(missingField), 121 "Field '$missingField' is required, but it was missing", 122 null 123 ) 124 } 125 126 /** 127 * Thrown when [KSerializer] received unknown property index from [CompositeDecoder.decodeElementIndex]. 128 * 129 * This exception means that data schema has changed in backwards-incompatible way. 130 */ 131 @PublishedApi 132 internal class UnknownFieldException 133 // This constructor is used by coroutines exception recovery 134 internal constructor(message: String?) : SerializationException(message) { 135 // This constructor is used by the generated serializers 136 constructor(index: Int) : this("An unknown field for index $index") 137 } 138