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

<lambda>null1 package kotlinx.serialization.features
2 
3 import kotlinx.serialization.*
4 import kotlinx.serialization.builtins.*
5 import kotlinx.serialization.json.*
6 import kotlinx.serialization.test.*
7 import kotlin.test.*
8 
9 
10 class JsonNamingStrategyTest : JsonTestBase() {
11     @Serializable
12     data class Foo(
13         val simple: String = "a",
14         val oneWord: String = "b",
15         val already_in_snake: String = "c",
16         val aLotOfWords: String = "d",
17         val FirstCapitalized: String = "e",
18         val hasAcronymURL: Bar = Bar.BAZ,
19         val hasDigit123AndPostfix: Bar = Bar.QUX,
20         val coercionTest: Bar = Bar.QUX
21     )
22 
23     enum class Bar { BAZ, QUX }
24 
25     val jsonWithNaming = Json(default) {
26         namingStrategy = JsonNamingStrategy.SnakeCase
27         decodeEnumsCaseInsensitive = true // check that related feature does not break anything
28     }
29 
30     @Test
31     fun testJsonNamingStrategyWithAlternativeNames() = doTest(Json(jsonWithNaming) {
32         useAlternativeNames = true
33     })
34 
35     @Test
36     fun testJsonNamingStrategyWithoutAlternativeNames() = doTest(Json(jsonWithNaming) {
37         useAlternativeNames = false
38     })
39 
40     private fun doTest(json: Json) {
41         val foo = Foo()
42         assertJsonFormAndRestored(
43             Foo.serializer(),
44             foo,
45             """{"simple":"a","one_word":"b","already_in_snake":"c","a_lot_of_words":"d","first_capitalized":"e","has_acronym_url":"BAZ","has_digit123_and_postfix":"QUX","coercion_test":"QUX"}""",
46             json
47         )
48     }
49 
50     @Test
51     fun testNamingStrategyWorksWithCoercing() {
52         val j = Json(jsonWithNaming) {
53             coerceInputValues = true
54             useAlternativeNames = false
55         }
56         assertEquals(
57             Foo(),
58             j.decodeFromString("""{"simple":"a","one_word":"b","already_in_snake":"c","a_lot_of_words":"d","first_capitalized":"e","has_acronym_url":"baz","has_digit123_and_postfix":"qux","coercion_test":"invalid"}""")
59         )
60     }
61 
62     @Test
63     fun testSnakeCaseStrategy() {
64         fun apply(name: String) =
65             JsonNamingStrategy.SnakeCase.serialNameForJson(String.serializer().descriptor, 0, name)
66 
67         val cases = mapOf<String, String>(
68             "" to "",
69             "_" to "_",
70             "___" to "___",
71             "a" to "a",
72             "A" to "a",
73             "_1" to "_1",
74             "_a" to "_a",
75             "_A" to "_a",
76             "property" to "property",
77             "twoWords" to "two_words",
78             "threeDistinctWords" to "three_distinct_words",
79             "ThreeDistinctWords" to "three_distinct_words",
80             "Oneword" to "oneword",
81             "camel_Case_Underscores" to "camel_case_underscores",
82             "_many____underscores__" to "_many____underscores__",
83             "URLmapping" to "ur_lmapping",
84             "URLMapping" to "url_mapping",
85             "IOStream" to "io_stream",
86             "IOstream" to "i_ostream",
87             "myIo2Stream" to "my_io2_stream",
88             "myIO2Stream" to "my_io2_stream",
89             "myIO2stream" to "my_io2stream",
90             "myIO2streamMax" to "my_io2stream_max",
91             "InURLBetween" to "in_url_between",
92             "myHTTP2APIKey" to "my_http2_api_key",
93             "myHTTP2fastApiKey" to "my_http2fast_api_key",
94             "myHTTP23APIKey" to "my_http23_api_key",
95             "myHttp23ApiKey" to "my_http23_api_key",
96             "theWWW" to "the_www",
97             "theWWW_URL_xxx" to "the_www_url_xxx",
98             "hasDigit123AndPostfix" to "has_digit123_and_postfix"
99         )
100 
101         cases.forEach { (input, expected) ->
102             assertEquals(expected, apply(input))
103         }
104     }
105 
106     @Test
107     fun testKebabCaseStrategy() {
108         fun apply(name: String) =
109             JsonNamingStrategy.KebabCase.serialNameForJson(String.serializer().descriptor, 0, name)
110 
111         val cases = mapOf<String, String>(
112             "" to "",
113             "_" to "_",
114             "-" to "-",
115             "___" to "___",
116             "---" to "---",
117             "a" to "a",
118             "A" to "a",
119             "-1" to "-1",
120             "-a" to "-a",
121             "-A" to "-a",
122             "property" to "property",
123             "twoWords" to "two-words",
124             "threeDistinctWords" to "three-distinct-words",
125             "ThreeDistinctWords" to "three-distinct-words",
126             "Oneword" to "oneword",
127             "camel-Case-WithDashes" to "camel-case-with-dashes",
128             "_many----dashes--" to "_many----dashes--",
129             "URLmapping" to "ur-lmapping",
130             "URLMapping" to "url-mapping",
131             "IOStream" to "io-stream",
132             "IOstream" to "i-ostream",
133             "myIo2Stream" to "my-io2-stream",
134             "myIO2Stream" to "my-io2-stream",
135             "myIO2stream" to "my-io2stream",
136             "myIO2streamMax" to "my-io2stream-max",
137             "InURLBetween" to "in-url-between",
138             "myHTTP2APIKey" to "my-http2-api-key",
139             "myHTTP2fastApiKey" to "my-http2fast-api-key",
140             "myHTTP23APIKey" to "my-http23-api-key",
141             "myHttp23ApiKey" to "my-http23-api-key",
142             "theWWW" to "the-www",
143             "theWWW-URL-xxx" to "the-www-url-xxx",
144             "hasDigit123AndPostfix" to "has-digit123-and-postfix"
145         )
146 
147         cases.forEach { (input, expected) ->
148             assertEquals(expected, apply(input))
149         }
150     }
151 
152     @Serializable
153     data class DontUseOriginal(val testCase: String)
154 
155     @Test
156     fun testNamingStrategyOverridesOriginal() {
157         val json = Json(jsonWithNaming) {
158             ignoreUnknownKeys = true
159         }
160         parametrizedTest { mode ->
161             assertEquals(DontUseOriginal("a"), json.decodeFromString("""{"test_case":"a","testCase":"b"}""", mode))
162         }
163 
164         val jsonThrows = Json(jsonWithNaming) {
165             ignoreUnknownKeys = false
166         }
167         parametrizedTest { mode ->
168             assertFailsWithMessage<SerializationException>("Encountered an unknown key 'testCase'") {
169                 jsonThrows.decodeFromString<DontUseOriginal>("""{"test_case":"a","testCase":"b"}""", mode)
170             }
171         }
172     }
173 
174     @Serializable
175     data class CollisionCheckPrimary(val testCase: String, val test_case: String)
176 
177     @Serializable
178     data class CollisionCheckAlternate(val testCase: String, @JsonNames("test_case") val testCase2: String)
179 
180     @Test
181     fun testNamingStrategyPrioritizesOverAlternative() {
182         val json = Json(jsonWithNaming) {
183             ignoreUnknownKeys = true
184         }
185         parametrizedTest { mode ->
186             assertFailsWithMessage<SerializationException>("The suggested name 'test_case' for property test_case is already one of the names for property testCase") {
187                 json.decodeFromString<CollisionCheckPrimary>("""{"test_case":"a"}""", mode)
188             }
189         }
190         parametrizedTest { mode ->
191             assertFailsWithMessage<SerializationException>("The suggested name 'test_case' for property testCase2 is already one of the names for property testCase") {
192                 json.decodeFromString<CollisionCheckAlternate>("""{"test_case":"a"}""", mode)
193             }
194         }
195     }
196 
197 
198     @Serializable
199     data class OriginalAsFallback(@JsonNames("testCase") val testCase: String)
200 
201     @Test
202     fun testCanUseOriginalNameAsAlternative() {
203         val json = Json(jsonWithNaming) {
204             ignoreUnknownKeys = true
205         }
206         parametrizedTest { mode ->
207             assertEquals(OriginalAsFallback("b"), json.decodeFromString("""{"testCase":"b"}""", mode))
208         }
209     }
210 
211     @Serializable
212     sealed interface SealedBase {
213         @Serializable
214         @JsonClassDiscriminator("typeSub")
215         sealed class SealedMid : SealedBase {
216             @Serializable
217             @SerialName("SealedSub1")
218             object SealedSub1 : SealedMid()
219         }
220 
221         @Serializable
222         @SerialName("SealedSub2")
223         data class SealedSub2(val testCase: Int = 0) : SealedBase
224     }
225 
226     @Serializable
227     data class Holder(val testBase: SealedBase, val testMid: SealedBase.SealedMid)
228 
229     @Test
230     fun testNamingStrategyDoesNotAffectPolymorphism() {
231         val json = Json(jsonWithNaming) {
232             classDiscriminator = "typeBase"
233         }
234         val holder = Holder(SealedBase.SealedSub2(), SealedBase.SealedMid.SealedSub1)
235         assertJsonFormAndRestored(
236             Holder.serializer(),
237             holder,
238             """{"test_base":{"typeBase":"SealedSub2","test_case":0},"test_mid":{"typeSub":"SealedSub1"}}""",
239             json
240         )
241     }
242 }
243