1 /* <lambda>null2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.serialization.json 6 7 import kotlinx.serialization.* 8 import kotlinx.serialization.test.assertFailsWithSerial 9 import kotlin.test.* 10 11 class JsonCoerceInputValuesTest : JsonTestBase() { 12 @Serializable 13 data class WithBoolean(val b: Boolean = false) 14 15 @Serializable 16 data class WithEnum(val e: SampleEnum = SampleEnum.OptionC) 17 18 @Serializable 19 data class MultipleValues( 20 val data: StringData, 21 val data2: IntData = IntData(0), 22 val i: Int = 42, 23 val e: SampleEnum = SampleEnum.OptionA, 24 val foo: String 25 ) 26 27 @Serializable 28 data class NullableEnumHolder( 29 val enum: SampleEnum? 30 ) 31 32 @Serializable 33 class Uncoercable( 34 val s: String 35 ) 36 37 @Serializable 38 class UncoercableEnum( 39 val e: SampleEnum 40 ) 41 42 @Serializable 43 data class NullableEnumWithDefault( 44 val e: SampleEnum? = SampleEnum.OptionC 45 ) 46 47 val json = Json { 48 coerceInputValues = true 49 isLenient = true 50 } 51 52 private val jsonNonLenient = Json(json) { isLenient = false } 53 54 55 private fun <T> doTest(inputs: List<String>, expected: T, serializer: KSerializer<T>) { 56 for (input in inputs) { 57 parametrizedTest(json) { 58 assertEquals(expected, decodeFromString(serializer, input), "Failed on input: $input") 59 } 60 } 61 } 62 63 @Test 64 fun testUseDefaultOnNonNullableBoolean() = doTest( 65 listOf( 66 """{"b":false}""", 67 """{"b":null}""", 68 """{}""", 69 ), 70 WithBoolean(), 71 WithBoolean.serializer() 72 ) 73 74 @Test 75 fun testUseDefaultOnUnknownEnum() { 76 doTest( 77 listOf( 78 """{"e":unknown_value}""", 79 """{"e":"unknown_value"}""", 80 """{"e":null}""", 81 """{}""", 82 ), 83 WithEnum(), 84 WithEnum.serializer() 85 ) 86 assertFailsWithSerial("JsonDecodingException") { 87 json.decodeFromString(WithEnum.serializer(), """{"e":{"x":"definitely not a valid enum value"}}""") 88 } 89 assertFailsWithSerial("JsonDecodingException") { // test user still sees exception on missing quotes 90 jsonNonLenient.decodeFromString(WithEnum.serializer(), """{"e":unknown_value}""") 91 } 92 } 93 94 @Test 95 fun testUseDefaultInMultipleCases() { 96 val testData = mapOf( 97 """{"data":{"data":"foo"},"data2":null,"i":null,"e":null,"foo":"bar"}""" to MultipleValues( 98 StringData("foo"), 99 foo = "bar" 100 ), 101 """{"data":{"data":"foo"},"data2":{"intV":42},"i":null,"e":null,"foo":"bar"}""" to MultipleValues( 102 StringData( 103 "foo" 104 ), IntData(42), foo = "bar" 105 ), 106 """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"NoOption","foo":"bar"}""" to MultipleValues( 107 StringData("foo"), 108 IntData(42), 109 i = 0, 110 foo = "bar" 111 ), 112 """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"OptionC","foo":"bar"}""" to MultipleValues( 113 StringData("foo"), 114 IntData(42), 115 i = 0, 116 e = SampleEnum.OptionC, 117 foo = "bar" 118 ), 119 ) 120 for ((input, expected) in testData) { 121 assertEquals(expected, json.decodeFromString(MultipleValues.serializer(), input), "Failed on input: $input") 122 } 123 } 124 125 @Test 126 fun testNullSupportForEnums() = parametrizedTest(json) { 127 var decoded = decodeFromString<NullableEnumHolder>("""{"enum": null}""") 128 assertNull(decoded.enum) 129 130 decoded = decodeFromString<NullableEnumHolder>("""{"enum": OptionA}""") 131 assertEquals(SampleEnum.OptionA, decoded.enum) 132 } 133 134 @Test 135 fun propertiesWithoutDefaultValuesDoNotChangeErrorMsg() { 136 val json2 = Json(json) { coerceInputValues = false } 137 parametrizedTest { mode -> 138 val e1 = assertFailsWith<SerializationException>() { json.decodeFromString<Uncoercable>("""{"s":null}""", mode) } 139 val e2 = assertFailsWith<SerializationException>() { json2.decodeFromString<Uncoercable>("""{"s":null}""", mode) } 140 assertEquals(e2.message, e1.message) 141 } 142 } 143 144 @Test 145 fun propertiesWithoutDefaultValuesDoNotChangeErrorMsgEnum() { 146 val json2 = Json(json) { coerceInputValues = false } 147 parametrizedTest { mode -> 148 val e1 = assertFailsWith<SerializationException> { json.decodeFromString<UncoercableEnum>("""{"e":"UNEXPECTED"}""", mode) } 149 val e2 = assertFailsWith<SerializationException> { json2.decodeFromString<UncoercableEnum>("""{"e":"UNEXPECTED"}""", mode) } 150 assertEquals(e2.message, e1.message) 151 } 152 } 153 154 @Test 155 fun testNullableEnumWithoutDefault() { 156 val j = Json(json) { explicitNulls = false } 157 parametrizedTest { mode -> 158 assertEquals(NullableEnumHolder(null), j.decodeFromString("{}")) 159 assertEquals(NullableEnumHolder(null), j.decodeFromString("""{"enum":"incorrect"}""")) 160 } 161 } 162 163 @Test 164 fun testNullableEnumWithoutDefaultDoesNotCoerceExplicitly() { 165 val j = Json(json) { explicitNulls = true } 166 parametrizedTest { mode -> 167 assertFailsWith<SerializationException> { j.decodeFromString<NullableEnumHolder>("{}") } 168 assertFailsWith<SerializationException> { j.decodeFromString<NullableEnumHolder>("""{"enum":"incorrect"}""") } 169 } 170 } 171 172 @Test 173 fun testNullableEnumWithDefault() { 174 val j = Json(json) { explicitNulls = false } 175 parametrizedTest { mode -> 176 assertEquals(NullableEnumWithDefault(), j.decodeFromString("{}")) 177 assertEquals(NullableEnumWithDefault(), j.decodeFromString("""{"e":"incorrect"}""")) 178 assertEquals(NullableEnumWithDefault(null), j.decodeFromString("""{"e":null}""")) 179 } 180 } 181 } 182