Skip to content

Commit caa194b

Browse files
committed
Merge remote-tracking branch 'origin/master' into dev
2 parents 3e54de5 + 5fb55ff commit caa194b

22 files changed

+333
-237
lines changed

RELEASING.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ If review is not required, commit directly to `dev`.
4141
* Close the repository and wait for it to verify.
4242
* Release it.
4343

44-
5. Propose the website documentation update:<br>
45-
* Set a new value for [`KOTLINX_SERIALIZATION_RELEASE_TAG`](https://github.com/JetBrains/kotlin-web-site/blob/master/.teamcity/BuildParams.kt), creating a pull request in the website's repository.
46-
* The website team will be notified about the pull request, review your changes, and merge it to master after all checks pass.
47-
* Once the pull request is merged to the main branch, it automatically will trigger the website's update, which will be done within an hour.
44+
5. Set a new value for [`KOTLINX_SERIALIZATION_RELEASE_TAG`](https://github.com/JetBrains/kotlin-web-site/blob/master/.teamcity/BuildParams.kt),
45+
creating a pull request in the website's repository. To find out why it is needed, [read this](#kotlinxserializationreleasetag).
4846

4947
6. Create a new release in [Github releases](https://github.com/Kotlin/kotlinx.serialization/releases). Use created git tag for title and changelog message for body.
5048

@@ -56,3 +54,25 @@ If review is not required, commit directly to `dev`.
5654
```
5755

5856
5. Announce new release in [Slack](https://kotlinlang.slack.com).
57+
58+
# API reference documentation
59+
60+
The [API reference documentation](https://kotlinlang.org/api/kotlinx.serialization/) is built and deployed automatically
61+
for every commit in `master`, typically within the same day.
62+
63+
**Note**: KDoc / API reference changes targeting `master` should not contain information which is irrelevant to or is
64+
incorrect in relation to the latest release, because these changes will be deployed live automatically, and they might
65+
confuse readers.
66+
67+
The build configuration responsible for assembling the documentation can be found
68+
[on TeamCity](https://buildserver.labs.intellij.net/buildConfiguration/Kotlin_KotlinSites_KotlinlangTeamcityDsl_KotlinxSerializationBuildApiReference).
69+
70+
### KOTLINX_SERIALIZATION_RELEASE_TAG
71+
72+
The generated API reference documentation has the library version specified in the header. By default, the value
73+
of the `version` project property is taken. However, this property usually contains the upcoming version with
74+
the `-SNAPSHOT` suffix, so it cannot be used if you want to publish the updated documentation of the latest release.
75+
76+
For this reason, the [`KOTLINX_SERIALIZATION_RELEASE_TAG`](https://github.com/JetBrains/kotlin-web-site/blob/master/.teamcity/BuildParams.kt)
77+
property must be set during every release: its value will be used for all subsequent publications of the API docs to kotlinlang.org,
78+
and it will appear in the header.

core/commonMain/src/kotlinx/serialization/Serializers.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public fun serializer(
108108
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
109109
* Star projections in [type]'s arguments are prohibited.
110110
*
111-
* @returns [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
111+
* @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
112112
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
113113
*/
114114
public fun serializerOrNull(type: KType): KSerializer<Any?>? = EmptySerializersModule().serializerOrNull(type)
@@ -174,7 +174,7 @@ public fun SerializersModule.serializer(
174174
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
175175
* Star projections in [type]'s arguments are prohibited.
176176
*
177-
* @returns [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable and is not registered in [this] module).
177+
* @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable and is not registered in [this] module).
178178
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
179179
*/
180180
public fun SerializersModule.serializerOrNull(type: KType): KSerializer<Any?>? =

core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package kotlinx.serialization.descriptors
66

77
import kotlinx.serialization.*
8+
import kotlinx.serialization.builtins.*
89
import kotlinx.serialization.encoding.*
910
import kotlinx.serialization.internal.*
1011
import kotlin.reflect.*
@@ -24,22 +25,24 @@ import kotlin.reflect.*
2425
* val nullableInt: Int?
2526
* )
2627
* // Descriptor for such class:
27-
* SerialDescriptor("my.package.Data") {
28+
* buildClassSerialDescriptor("my.package.Data") {
2829
* // intField is deliberately ignored by serializer -- not present in the descriptor as well
2930
* element<Long>("_longField") // longField is named as _longField
30-
* element("stringField", listSerialDescriptor<String>())
31+
* element("stringField", listSerialDescriptor<String>()) // or ListSerializer(String.serializer()).descriptor
3132
* element("nullableInt", serialDescriptor<Int>().nullable)
3233
* }
3334
* ```
3435
*
3536
* Example for generic classes:
3637
* ```
38+
* import kotlinx.serialization.builtins.*
39+
*
3740
* @Serializable(CustomSerializer::class)
3841
* class BoxedList<T>(val list: List<T>)
3942
*
4043
* class CustomSerializer<T>(tSerializer: KSerializer<T>): KSerializer<BoxedList<T>> {
4144
* // here we use tSerializer.descriptor because it represents T
42-
* override val descriptor = SerialDescriptor("pkg.BoxedList", CLASS, tSerializer.descriptor) {
45+
* override val descriptor = buildClassSerialDescriptor("pkg.BoxedList", tSerializer.descriptor) {
4346
* // here we have to wrap it with List first, because property has type List<T>
4447
* element("list", ListSerializer(tSerializer).descriptor) // or listSerialDescriptor(tSerializer.descriptor)
4548
* }
@@ -129,7 +132,7 @@ internal class WrappedSerialDescriptor(override val serialName: String, original
129132
* This function is left public only for migration of pre-release users and is not intended to be used
130133
* as generally-safe and stable mechanism. Beware that it can produce inconsistent or non spec-compliant instances.
131134
*
132-
* If you end up using this builder, please file an issue with your use-case in kotlinx.serialization
135+
* If you end up using this builder, please file an issue with your use-case in kotlinx.serialization issue tracker.
133136
*/
134137
@InternalSerializationApi
135138
@OptIn(ExperimentalSerializationApi::class)

docs/json.md

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ In this chapter, we'll walk through features of [JSON](https://www.json.org/json
2020
* [Allowing structured map keys](#allowing-structured-map-keys)
2121
* [Allowing special floating-point values](#allowing-special-floating-point-values)
2222
* [Class discriminator for polymorphism](#class-discriminator-for-polymorphism)
23+
* [Global naming strategy](#global-naming-strategy)
2324
* [Json elements](#json-elements)
2425
* [Parsing to Json element](#parsing-to-json-element)
2526
* [Types of Json elements](#types-of-json-elements)
@@ -468,6 +469,53 @@ As you can see, discriminator from the `Base` class is used:
468469

469470
<!--- TEST -->
470471

472+
### Global naming strategy
473+
474+
If properties' names in Json input are different from Kotlin ones, it is recommended to specify the name
475+
for each property explicitly using [`@SerialName` annotation](basic-serialization.md#serial-field-names).
476+
However, there are certain situations where transformation should be applied to every serial name — such as migration
477+
from other frameworks or legacy codebase. For that cases, it is possible to specify a [namingStrategy][JsonBuilder.namingStrategy]
478+
for a [Json] instance. `kotlinx.serialization` provides one strategy implementation out of the box, the [JsonNamingStrategy.SnakeCase](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/-builtins/-snake-case.html):
479+
480+
```kotlin
481+
@Serializable
482+
data class Project(val projectName: String, val projectOwner: String)
483+
484+
val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
485+
486+
fun main() {
487+
val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""")
488+
println(format.encodeToString(project.copy(projectName = "kotlinx.serialization")))
489+
}
490+
```
491+
492+
> You can get the full code [here](../guide/example/example-json-12.kt).
493+
494+
As you can see, both serialization and deserialization work as if all serial names are transformed from camel case to snake case:
495+
496+
```text
497+
{"project_name":"kotlinx.serialization","project_owner":"Kotlin"}
498+
```
499+
500+
There are some caveats one should remember while dealing with a [JsonNamingStrategy]:
501+
502+
* Due to the nature of the `kotlinx.serialization` framework, naming strategy transformation is applied to all properties regardless
503+
of whether their serial name was taken from the property name or provided by [SerialName] annotation.
504+
Effectively, it means one cannot avoid transformation by explicitly specifying the serial name. To be able to deserialize
505+
non-transformed names, [JsonNames] annotation can be used instead.
506+
507+
* Collision of the transformed name with any other (transformed) properties serial names or any alternative names
508+
specified with [JsonNames] will lead to a deserialization exception.
509+
510+
* Global naming strategies are very implicit: by looking only at the definition of the class,
511+
it is impossible to determine which names it will have in the serialized form.
512+
As a consequence, naming strategies are not friendly to actions like Find Usages/Rename in IDE, full-text search by grep, etc.
513+
For them, the original name and the transformed are two different things;
514+
changing one without the other may introduce bugs in many unexpected ways and lead to greater maintenance efforts for code with global naming strategies.
515+
516+
Therefore, one should carefully weigh the pros and cons before considering adding global naming strategies to an application.
517+
518+
<!--- TEST -->
471519

472520
## Json elements
473521

@@ -493,7 +541,7 @@ fun main() {
493541
}
494542
```
495543

496-
> You can get the full code [here](../guide/example/example-json-12.kt).
544+
> You can get the full code [here](../guide/example/example-json-13.kt).
497545
498546
A `JsonElement` prints itself as a valid JSON:
499547

@@ -536,7 +584,7 @@ fun main() {
536584
}
537585
```
538586

539-
> You can get the full code [here](../guide/example/example-json-13.kt).
587+
> You can get the full code [here](../guide/example/example-json-14.kt).
540588
541589
The above example sums `votes` in all objects in the `forks` array, ignoring the objects that have no `votes`:
542590

@@ -576,7 +624,7 @@ fun main() {
576624
}
577625
```
578626

579-
> You can get the full code [here](../guide/example/example-json-14.kt).
627+
> You can get the full code [here](../guide/example/example-json-15.kt).
580628
581629
As a result, you get a proper JSON string:
582630

@@ -605,7 +653,7 @@ fun main() {
605653
}
606654
```
607655

608-
> You can get the full code [here](../guide/example/example-json-15.kt).
656+
> You can get the full code [here](../guide/example/example-json-16.kt).
609657
610658
The result is exactly what you would expect:
611659

@@ -651,7 +699,7 @@ fun main() {
651699
}
652700
```
653701

654-
> You can get the full code [here](../guide/example/example-json-16.kt).
702+
> You can get the full code [here](../guide/example/example-json-17.kt).
655703
656704
Even though `pi` was defined as a number with 30 decimal places, the resulting JSON does not reflect this.
657705
The [Double] value is truncated to 15 decimal places, and the String is wrapped in quotes - which is not a JSON number.
@@ -691,7 +739,7 @@ fun main() {
691739
}
692740
```
693741

694-
> You can get the full code [here](../guide/example/example-json-17.kt).
742+
> You can get the full code [here](../guide/example/example-json-18.kt).
695743
696744
`pi_literal` now accurately matches the value defined.
697745

@@ -731,7 +779,7 @@ fun main() {
731779
}
732780
```
733781

734-
> You can get the full code [here](../guide/example/example-json-18.kt).
782+
> You can get the full code [here](../guide/example/example-json-19.kt).
735783
736784
The exact value of `pi` is decoded, with all 30 decimal places of precision that were in the source JSON.
737785

@@ -753,7 +801,7 @@ fun main() {
753801
}
754802
```
755803

756-
> You can get the full code [here](../guide/example/example-json-19.kt).
804+
> You can get the full code [here](../guide/example/example-json-20.kt).
757805
758806
```text
759807
Exception in thread "main" kotlinx.serialization.json.internal.JsonEncodingException: Creating a literal unquoted value of 'null' is forbidden. If you want to create JSON null literal, use JsonNull object, otherwise, use JsonPrimitive
@@ -829,7 +877,7 @@ fun main() {
829877
}
830878
```
831879

832-
> You can get the full code [here](../guide/example/example-json-20.kt).
880+
> You can get the full code [here](../guide/example/example-json-21.kt).
833881
834882
The output shows that both cases are correctly deserialized into a Kotlin [List].
835883

@@ -881,7 +929,7 @@ fun main() {
881929
}
882930
```
883931

884-
> You can get the full code [here](../guide/example/example-json-21.kt).
932+
> You can get the full code [here](../guide/example/example-json-22.kt).
885933
886934
You end up with a single JSON object, not an array with one element:
887935

@@ -926,7 +974,7 @@ fun main() {
926974
}
927975
```
928976

929-
> You can get the full code [here](../guide/example/example-json-22.kt).
977+
> You can get the full code [here](../guide/example/example-json-23.kt).
930978
931979
See the effect of the custom serializer:
932980

@@ -999,7 +1047,7 @@ fun main() {
9991047
}
10001048
```
10011049

1002-
> You can get the full code [here](../guide/example/example-json-23.kt).
1050+
> You can get the full code [here](../guide/example/example-json-24.kt).
10031051
10041052
No class discriminator is added in the JSON output:
10051053

@@ -1095,7 +1143,7 @@ fun main() {
10951143
}
10961144
```
10971145

1098-
> You can get the full code [here](../guide/example/example-json-24.kt).
1146+
> You can get the full code [here](../guide/example/example-json-25.kt).
10991147
11001148
This gives you fine-grained control on the representation of the `Response` class in the JSON output:
11011149

@@ -1160,7 +1208,7 @@ fun main() {
11601208
}
11611209
```
11621210

1163-
> You can get the full code [here](../guide/example/example-json-25.kt).
1211+
> You can get the full code [here](../guide/example/example-json-26.kt).
11641212
11651213
```text
11661214
UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"})
@@ -1214,6 +1262,8 @@ The next chapter covers [Alternative and custom formats (experimental)](formats.
12141262
[JsonBuilder.allowSpecialFloatingPointValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html
12151263
[JsonBuilder.classDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html
12161264
[JsonClassDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/index.html
1265+
[JsonBuilder.namingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/naming-strategy.html
1266+
[JsonNamingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/index.html
12171267
[JsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/index.html
12181268
[Json.parseToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/parse-to-json-element.html
12191269
[JsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/index.html

docs/serialization-guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ Once the project is set up, we can start serializing some classes.
119119
* <a name='allowing-structured-map-keys'></a>[Allowing structured map keys](json.md#allowing-structured-map-keys)
120120
* <a name='allowing-special-floating-point-values'></a>[Allowing special floating-point values](json.md#allowing-special-floating-point-values)
121121
* <a name='class-discriminator-for-polymorphism'></a>[Class discriminator for polymorphism](json.md#class-discriminator-for-polymorphism)
122+
* <a name='global-naming-strategy'></a>[Global naming strategy](json.md#global-naming-strategy)
122123
* <a name='json-elements'></a>[Json elements](json.md#json-elements)
123124
* <a name='parsing-to-json-element'></a>[Parsing to Json element](json.md#parsing-to-json-element)
124125
* <a name='types-of-json-elements'></a>[Types of Json elements](json.md#types-of-json-elements)

formats/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ For convenience, they have same `groupId`, versioning and release cycle as core
3131
| android.os.Bundle | [AhmedMourad0/bundlizer](https://github.com/AhmedMourad0/bundlizer) <br> `dev.ahmedmourad.bundlizer:bundlizer-core` | Android | Allow serialization and deserialization of objects to and from [android.os.Bundle](https://developer.android.com/reference/android/os/Bundle). |
3232
| CSV | [hfhbd/kotlinx-serialization-csv](https://github.com/hfhbd/kotlinx-serialization-csv) <br> `app.softwork:kotlinx-serialization-csv` | all supported platforms | Allows serialization and deserialization of CSV files. There are still some limitations (ordered properties). |
3333
| Fixed Length Format | [hfhbd/kotlinx-serialization-csv](https://github.com/hfhbd/kotlinx-serialization-csv) <br> `app.softwork:kotlinx-serialization-flf` | all supported platforms | Allows serialization and deserialization of [Fixed Length Format files](https://www.ibm.com/docs/en/psfa/7.2.1?topic=format-fixed-length-files). Each property must be annotated with `@FixedLength` and there are still some limitations due to missing delimiters. |
34-
| JSON5 | [xn32/json5k](https://github.com/xn32/json5k) <br> `io.github.xn32:json5k` | JVM only | Library for the serialization to and deserialization from [JSON5](https://json5.org) text. |
34+
| JSON5 | [xn32/json5k](https://github.com/xn32/json5k) <br> `io.github.xn32:json5k` | JVM, Native | Library for the serialization to and deserialization from [JSON5](https://json5.org) text. |

guide/example/example-json-12.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ package example.exampleJson12
44
import kotlinx.serialization.*
55
import kotlinx.serialization.json.*
66

7+
@Serializable
8+
data class Project(val projectName: String, val projectOwner: String)
9+
10+
val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
11+
712
fun main() {
8-
val element = Json.parseToJsonElement("""
9-
{"name":"kotlinx.serialization","language":"Kotlin"}
10-
""")
11-
println(element)
13+
val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""")
14+
println(format.encodeToString(project.copy(projectName = "kotlinx.serialization")))
1215
}

0 commit comments

Comments
 (0)