• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package kotlinx.serialization.features
2 
3 import kotlinx.serialization.*
4 import kotlinx.serialization.json.*
5 import kotlinx.serialization.test.*
6 import kotlin.test.*
7 
8 @Suppress("EnumEntryName")
9 class JsonEnumsCaseInsensitiveTest: JsonTestBase() {
10     @Serializable
11     data class Foo(
12         val one: Bar = Bar.BAZ,
13         val two: Bar = Bar.QUX,
14         val three: Bar = Bar.QUX
15     )
16 
17     enum class Bar { BAZ, QUX }
18 
19     // It seems that we no longer report a warning that @Serializable is required for enums with @SerialName.
20     // It is still required for them to work at top-level.
21     @Serializable
22     enum class Cases {
23         ALL_CAPS,
24         MiXed,
25         all_lower,
26 
27         @JsonNames("AltName")
28         hasAltNames,
29 
30         @SerialName("SERIAL_NAME")
31         hasSerialName
32     }
33 
34     @Serializable
35     data class EnumCases(val cases: List<Cases>)
36 
37     val json = Json(default) { decodeEnumsCaseInsensitive = true }
38 
39     @Test
40     fun testCases() = parametrizedTest { mode ->
41         val input =
42             """{"cases":["ALL_CAPS","all_caps","mixed","MIXED","miXed","all_lower","ALL_LOWER","all_Lower","hasAltNames","HASALTNAMES","altname","ALTNAME","AltName","SERIAL_NAME","serial_name"]}"""
43         val target = listOf(
44             Cases.ALL_CAPS,
45             Cases.ALL_CAPS,
46             Cases.MiXed,
47             Cases.MiXed,
48             Cases.MiXed,
49             Cases.all_lower,
50             Cases.all_lower,
51             Cases.all_lower,
52             Cases.hasAltNames,
53             Cases.hasAltNames,
54             Cases.hasAltNames,
55             Cases.hasAltNames,
56             Cases.hasAltNames,
57             Cases.hasSerialName,
58             Cases.hasSerialName
59         )
60         val decoded = json.decodeFromString<EnumCases>(input, mode)
61         assertEquals(EnumCases(target), decoded)
62         val encoded = json.encodeToString(decoded, mode)
63         assertEquals(
64             """{"cases":["ALL_CAPS","ALL_CAPS","MiXed","MiXed","MiXed","all_lower","all_lower","all_lower","hasAltNames","hasAltNames","hasAltNames","hasAltNames","hasAltNames","SERIAL_NAME","SERIAL_NAME"]}""",
65             encoded
66         )
67     }
68 
69     @Test
70     fun testTopLevelList() = parametrizedTest { mode ->
71         val input = """["all_caps","serial_name"]"""
72         val decoded = json.decodeFromString<List<Cases>>(input, mode)
73         assertEquals(listOf(Cases.ALL_CAPS, Cases.hasSerialName), decoded)
74         assertEquals("""["ALL_CAPS","SERIAL_NAME"]""", json.encodeToString(decoded, mode))
75     }
76 
77     @Test
78     fun testTopLevelEnum() = parametrizedTest { mode ->
79         val input = """"altName""""
80         val decoded = json.decodeFromString<Cases>(input, mode)
81         assertEquals(Cases.hasAltNames, decoded)
82         assertEquals(""""hasAltNames"""", json.encodeToString(decoded, mode))
83     }
84 
85     @Test
86     fun testSimpleCase() = parametrizedTest { mode ->
87         val input = """{"one":"baz","two":"Qux","three":"QUX"}"""
88         val decoded = json.decodeFromString<Foo>(input, mode)
89         assertEquals(Foo(), decoded)
90         assertEquals("""{"one":"BAZ","two":"QUX","three":"QUX"}""", json.encodeToString(decoded, mode))
91     }
92 
93     enum class E { VALUE_A, @JsonNames("ALTERNATIVE") VALUE_B }
94 
95     @Test
96     fun testDocSample() {
97 
98         val j = Json { decodeEnumsCaseInsensitive = true }
99         @Serializable
100         data class Outer(val enums: List<E>)
101 
102         println(j.decodeFromString<Outer>("""{"enums":["value_A", "alternative"]}""").enums)
103     }
104 
105     @Test
106     fun testCoercingStillWorks() = parametrizedTest { mode ->
107         val withCoercing = Json(json) { coerceInputValues = true }
108         val input = """{"one":"baz","two":"unknown","three":"Que"}"""
109         assertEquals(Foo(),  withCoercing.decodeFromString<Foo>(input, mode))
110     }
111 
112     @Test
113     fun testCaseInsensitivePriorityOverCoercing() = parametrizedTest { mode ->
114         val withCoercing = Json(json) { coerceInputValues = true }
115         val input = """{"one":"QuX","two":"Baz","three":"Que"}"""
116         assertEquals(Foo(Bar.QUX, Bar.BAZ, Bar.QUX),  withCoercing.decodeFromString<Foo>(input, mode))
117     }
118 
119     @Test
120     fun testCoercingStillWorksWithNulls() = parametrizedTest { mode ->
121         val withCoercing = Json(json) { coerceInputValues = true }
122         val input = """{"one":"baz","two":"null","three":null}"""
123         assertEquals(Foo(),  withCoercing.decodeFromString<Foo>(input, mode))
124     }
125 
126     @Test
127     fun testFeatureDisablesProperly() = parametrizedTest { mode ->
128         val disabled = Json(json) {
129             coerceInputValues = true
130             decodeEnumsCaseInsensitive = false
131         }
132         val input = """{"one":"BAZ","two":"BAz","three":"baz"}""" // two and three should be coerced to QUX
133         assertEquals(Foo(), disabled.decodeFromString<Foo>(input, mode))
134     }
135 
136     @Test
137     fun testFeatureDisabledThrowsWithoutCoercing() = parametrizedTest { mode ->
138         val disabled = Json(json) {
139             coerceInputValues = false
140             decodeEnumsCaseInsensitive = false
141         }
142         val input = """{"one":"BAZ","two":"BAz","three":"baz"}"""
143         assertFailsWithMessage<SerializationException>("does not contain element with name 'BAz'") {
144             disabled.decodeFromString<Foo>(input, mode)
145         }
146     }
147 
148     @Serializable enum class BadEnum { Bad, BAD }
149 
150     @Serializable data class ListBadEnum(val l: List<BadEnum>)
151 
152     @Test
153     fun testLowercaseClashThrowsException() = parametrizedTest { mode ->
154         assertFailsWithMessage<SerializationException>("""The suggested name 'bad' for enum value BAD is already one of the names for enum value Bad""") {
155             json.decodeFromString<Box<BadEnum>>("""{"boxed":"bad"}""", mode)
156         }
157         assertFailsWithMessage<SerializationException>("""The suggested name 'bad' for enum value BAD is already one of the names for enum value Bad""") {
158             json.decodeFromString<Box<BadEnum>>("""{"boxed":"unrelated"}""", mode)
159         }
160     }
161 
162     @Test
163     fun testLowercaseClashHandledWithoutFeature() = parametrizedTest { mode ->
164         val disabled = Json(json) {
165             coerceInputValues = false
166             decodeEnumsCaseInsensitive = false
167         }
168         assertEquals(ListBadEnum(listOf(BadEnum.Bad, BadEnum.BAD)), disabled.decodeFromString("""{"l":["Bad","BAD"]}""", mode))
169     }
170 }
171