Skip to content

Commit c2303d9

Browse files
authored
Updated code examples and documentation to mention where @Serializable annotation can be applied directly to the types. (#2397)
Fixes #2384
1 parent 8baa04b commit c2303d9

11 files changed

+170
-94
lines changed

docs/serialization-guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Once the project is set up, we can start serializing some classes.
7171
* <a name='serializing-3rd-party-classes'></a>[Serializing 3rd party classes](serializers.md#serializing-3rd-party-classes)
7272
* <a name='passing-a-serializer-manually'></a>[Passing a serializer manually](serializers.md#passing-a-serializer-manually)
7373
* <a name='specifying-serializer-on-a-property'></a>[Specifying serializer on a property](serializers.md#specifying-serializer-on-a-property)
74+
* <a name='specifying-serializer-for-a-particular-type'></a>[Specifying serializer for a particular type](serializers.md#specifying-serializer-for-a-particular-type)
7475
* <a name='specifying-serializers-for-a-file'></a>[Specifying serializers for a file](serializers.md#specifying-serializers-for-a-file)
7576
* <a name='specifying-serializer-globally-using-typealias'></a>[Specifying serializer globally using typealias](serializers.md#specifying-serializer-globally-using-typealias)
7677
* <a name='custom-serializers-for-a-generic-type'></a>[Custom serializers for a generic type](serializers.md#custom-serializers-for-a-generic-type)

docs/serializers.md

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ In this chapter we'll take a look at serializers in more detail, and we'll see h
2424
* [Serializing 3rd party classes](#serializing-3rd-party-classes)
2525
* [Passing a serializer manually](#passing-a-serializer-manually)
2626
* [Specifying serializer on a property](#specifying-serializer-on-a-property)
27+
* [Specifying serializer for a particular type](#specifying-serializer-for-a-particular-type)
2728
* [Specifying serializers for a file](#specifying-serializers-for-a-file)
2829
* [Specifying serializer globally using typealias](#specifying-serializer-globally-using-typealias)
2930
* [Custom serializers for a generic type](#custom-serializers-for-a-generic-type)
@@ -713,7 +714,7 @@ because we don't control the `Date` source code. There are several ways to work
713714
### Passing a serializer manually
714715

715716
All `encodeToXxx` and `decodeFromXxx` functions have an overload with the first serializer parameter.
716-
When a non-serializable class, like `Date`, is the top-level class being serialized we can use those.
717+
When a non-serializable class, like `Date`, is the top-level class being serialized, we can use those.
717718

718719
```kotlin
719720
fun main() {
@@ -770,6 +771,45 @@ The `stableReleaseDate` property is serialized with the serialization strategy t
770771

771772
<!--- TEST -->
772773

774+
### Specifying serializer for a particular type
775+
776+
[`@Serializable`][Serializable] annotation can also be applied directly to the types.
777+
This is handy when a class that requires a custom serializer, such as `Date`, happens to be a generic type argument.
778+
The most common use case for that is when you have a list of dates:
779+
780+
<!--- INCLUDE
781+
import java.util.Date
782+
import java.text.SimpleDateFormat
783+
784+
object DateAsLongSerializer : KSerializer<Date> {
785+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
786+
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
787+
override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
788+
}
789+
-->
790+
791+
```kotlin
792+
@Serializable
793+
class ProgrammingLanguage(
794+
val name: String,
795+
val releaseDates: List<@Serializable(DateAsLongSerializer::class) Date>
796+
)
797+
798+
fun main() {
799+
val df = SimpleDateFormat("yyyy-MM-ddX")
800+
val data = ProgrammingLanguage("Kotlin", listOf(df.parse("2023-07-06+00"), df.parse("2023-04-25+00"), df.parse("2022-12-28+00")))
801+
println(Json.encodeToString(data))
802+
}
803+
```
804+
805+
> You can get the full code [here](../guide/example/example-serializer-16.kt).
806+
807+
```text
808+
{"name":"Kotlin","releaseDates":[1688601600000,1682380800000,1672185600000]}
809+
```
810+
811+
<!--- TEST -->
812+
773813
### Specifying serializers for a file
774814

775815
A serializer for a specific type, like `Date`, can be specified for a whole source code file with the file-level
@@ -803,7 +843,7 @@ fun main() {
803843
println(Json.encodeToString(data))
804844
}
805845
```
806-
> You can get the full code [here](../guide/example/example-serializer-16.kt).
846+
> You can get the full code [here](../guide/example/example-serializer-17.kt).
807847

808848
```text
809849
{"name":"Kotlin","stableReleaseDate":1455494400000}
@@ -857,7 +897,7 @@ fun main() {
857897
}
858898
```
859899

860-
> You can get the full code [here](../guide/example/example-serializer-17.kt).
900+
> You can get the full code [here](../guide/example/example-serializer-18.kt).
861901

862902
```text
863903
{"stableReleaseDate":"2016-02-15","lastReleaseTimestamp":1657152000000}
@@ -905,7 +945,7 @@ fun main() {
905945
}
906946
```
907947
908-
> You can get the full code [here](../guide/example/example-serializer-18.kt).
948+
> You can get the full code [here](../guide/example/example-serializer-19.kt).
909949
910950
The resulting JSON looks like the `Project` class was serialized directly.
911951
@@ -969,7 +1009,7 @@ fun main() {
9691009
To actually serialize this class we must provide the corresponding context when calling the `encodeToXxx`/`decodeFromXxx`
9701010
functions. Without it we'll get a "Serializer for class 'Date' is not found" exception.
9711011

972-
> See [here](../guide/example/example-serializer-19.kt) for an example that produces that exception.
1012+
> See [here](../guide/example/example-serializer-20.kt) for an example that produces that exception.
9731013

9741014
<!--- TEST LINES_START
9751015
Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Date' is not found.
@@ -1028,7 +1068,7 @@ fun main() {
10281068
}
10291069
```
10301070

1031-
> You can get the full code [here](../guide/example/example-serializer-20.kt).
1071+
> You can get the full code [here](../guide/example/example-serializer-21.kt).
10321072
```text
10331073
{"name":"Kotlin","stableReleaseDate":1455494400000}
10341074
```
@@ -1087,7 +1127,7 @@ fun main() {
10871127
}
10881128
```
10891129

1090-
> You can get the full code [here](../guide/example/example-serializer-21.kt).
1130+
> You can get the full code [here](../guide/example/example-serializer-22.kt).
10911131

10921132
This gets all the `Project` properties serialized:
10931133

@@ -1128,7 +1168,7 @@ fun main() {
11281168
}
11291169
```
11301170

1131-
> You can get the full code [here](../guide/example/example-serializer-22.kt).
1171+
> You can get the full code [here](../guide/example/example-serializer-23.kt).
11321172

11331173
The output is shown below.
11341174

guide/example/example-serializer-16.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
@file:UseSerializers(DateAsLongSerializer::class)
21
// This file was automatically generated from serializers.md by Knit tool. Do not edit.
32
package example.exampleSerializer16
43

@@ -17,9 +16,13 @@ object DateAsLongSerializer : KSerializer<Date> {
1716
}
1817

1918
@Serializable
20-
class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)
19+
class ProgrammingLanguage(
20+
val name: String,
21+
val releaseDates: List<@Serializable(DateAsLongSerializer::class) Date>
22+
)
2123

2224
fun main() {
23-
val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
25+
val df = SimpleDateFormat("yyyy-MM-ddX")
26+
val data = ProgrammingLanguage("Kotlin", listOf(df.parse("2023-07-06+00"), df.parse("2023-04-25+00"), df.parse("2022-12-28+00")))
2427
println(Json.encodeToString(data))
2528
}
Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@file:UseSerializers(DateAsLongSerializer::class)
12
// This file was automatically generated from serializers.md by Knit tool. Do not edit.
23
package example.exampleSerializer17
34

@@ -10,27 +11,15 @@ import java.util.Date
1011
import java.text.SimpleDateFormat
1112

1213
object DateAsLongSerializer : KSerializer<Date> {
13-
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsLong", PrimitiveKind.LONG)
14+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
1415
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
1516
override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
1617
}
1718

18-
object DateAsSimpleTextSerializer: KSerializer<Date> {
19-
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsSimpleText", PrimitiveKind.LONG)
20-
private val format = SimpleDateFormat("yyyy-MM-dd")
21-
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(format.format(value))
22-
override fun deserialize(decoder: Decoder): Date = format.parse(decoder.decodeString())
23-
}
24-
25-
typealias DateAsLong = @Serializable(DateAsLongSerializer::class) Date
26-
27-
typealias DateAsText = @Serializable(DateAsSimpleTextSerializer::class) Date
28-
2919
@Serializable
30-
class ProgrammingLanguage(val stableReleaseDate: DateAsText, val lastReleaseTimestamp: DateAsLong)
20+
class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)
3121

3222
fun main() {
33-
val format = SimpleDateFormat("yyyy-MM-ddX")
34-
val data = ProgrammingLanguage(format.parse("2016-02-15+00"), format.parse("2022-07-07+00"))
23+
val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
3524
println(Json.encodeToString(data))
3625
}

guide/example/example-serializer-18.kt

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,31 @@ import kotlinx.serialization.json.*
66
import kotlinx.serialization.encoding.*
77
import kotlinx.serialization.descriptors.*
88

9-
@Serializable(with = BoxSerializer::class)
10-
data class Box<T>(val contents: T)
9+
import java.util.Date
10+
import java.text.SimpleDateFormat
11+
12+
object DateAsLongSerializer : KSerializer<Date> {
13+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsLong", PrimitiveKind.LONG)
14+
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
15+
override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
16+
}
1117

12-
class BoxSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Box<T>> {
13-
override val descriptor: SerialDescriptor = dataSerializer.descriptor
14-
override fun serialize(encoder: Encoder, value: Box<T>) = dataSerializer.serialize(encoder, value.contents)
15-
override fun deserialize(decoder: Decoder) = Box(dataSerializer.deserialize(decoder))
18+
object DateAsSimpleTextSerializer: KSerializer<Date> {
19+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsSimpleText", PrimitiveKind.LONG)
20+
private val format = SimpleDateFormat("yyyy-MM-dd")
21+
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(format.format(value))
22+
override fun deserialize(decoder: Decoder): Date = format.parse(decoder.decodeString())
1623
}
1724

18-
@Serializable
19-
data class Project(val name: String)
25+
typealias DateAsLong = @Serializable(DateAsLongSerializer::class) Date
26+
27+
typealias DateAsText = @Serializable(DateAsSimpleTextSerializer::class) Date
28+
29+
@Serializable
30+
class ProgrammingLanguage(val stableReleaseDate: DateAsText, val lastReleaseTimestamp: DateAsLong)
2031

2132
fun main() {
22-
val box = Box(Project("kotlinx.serialization"))
23-
val string = Json.encodeToString(box)
24-
println(string)
25-
println(Json.decodeFromString<Box<Project>>(string))
33+
val format = SimpleDateFormat("yyyy-MM-ddX")
34+
val data = ProgrammingLanguage(format.parse("2016-02-15+00"), format.parse("2022-07-07+00"))
35+
println(Json.encodeToString(data))
2636
}

guide/example/example-serializer-19.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ import kotlinx.serialization.json.*
66
import kotlinx.serialization.encoding.*
77
import kotlinx.serialization.descriptors.*
88

9-
import java.util.Date
10-
import java.text.SimpleDateFormat
9+
@Serializable(with = BoxSerializer::class)
10+
data class Box<T>(val contents: T)
1111

12-
@Serializable
13-
class ProgrammingLanguage(
14-
val name: String,
15-
@Contextual
16-
val stableReleaseDate: Date
17-
)
12+
class BoxSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Box<T>> {
13+
override val descriptor: SerialDescriptor = dataSerializer.descriptor
14+
override fun serialize(encoder: Encoder, value: Box<T>) = dataSerializer.serialize(encoder, value.contents)
15+
override fun deserialize(decoder: Decoder) = Box(dataSerializer.deserialize(decoder))
16+
}
17+
18+
@Serializable
19+
data class Project(val name: String)
1820

1921
fun main() {
20-
val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
21-
println(Json.encodeToString(data))
22+
val box = Box(Project("kotlinx.serialization"))
23+
val string = Json.encodeToString(box)
24+
println(string)
25+
println(Json.decodeFromString<Box<Project>>(string))
2226
}

guide/example/example-serializer-20.kt

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,8 @@ import kotlinx.serialization.json.*
66
import kotlinx.serialization.encoding.*
77
import kotlinx.serialization.descriptors.*
88

9-
import kotlinx.serialization.modules.*
109
import java.util.Date
1110
import java.text.SimpleDateFormat
12-
13-
object DateAsLongSerializer : KSerializer<Date> {
14-
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
15-
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
16-
override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
17-
}
1811

1912
@Serializable
2013
class ProgrammingLanguage(
@@ -23,13 +16,7 @@ class ProgrammingLanguage(
2316
val stableReleaseDate: Date
2417
)
2518

26-
private val module = SerializersModule {
27-
contextual(DateAsLongSerializer)
28-
}
29-
30-
val format = Json { serializersModule = module }
31-
3219
fun main() {
3320
val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
34-
println(format.encodeToString(data))
21+
println(Json.encodeToString(data))
3522
}

guide/example/example-serializer-21.kt

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,30 @@ import kotlinx.serialization.json.*
66
import kotlinx.serialization.encoding.*
77
import kotlinx.serialization.descriptors.*
88

9-
// NOT @Serializable
10-
class Project(val name: String, val language: String)
11-
12-
@Serializer(forClass = Project::class)
13-
object ProjectSerializer
9+
import kotlinx.serialization.modules.*
10+
import java.util.Date
11+
import java.text.SimpleDateFormat
12+
13+
object DateAsLongSerializer : KSerializer<Date> {
14+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
15+
override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
16+
override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
17+
}
18+
19+
@Serializable
20+
class ProgrammingLanguage(
21+
val name: String,
22+
@Contextual
23+
val stableReleaseDate: Date
24+
)
25+
26+
private val module = SerializersModule {
27+
contextual(DateAsLongSerializer)
28+
}
29+
30+
val format = Json { serializersModule = module }
1431

1532
fun main() {
16-
val data = Project("kotlinx.serialization", "Kotlin")
17-
println(Json.encodeToString(ProjectSerializer, data))
33+
val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
34+
println(format.encodeToString(data))
1835
}

guide/example/example-serializer-22.kt

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,13 @@ import kotlinx.serialization.json.*
66
import kotlinx.serialization.encoding.*
77
import kotlinx.serialization.descriptors.*
88

9-
// NOT @Serializable, will use external serializer
10-
class Project(
11-
// val in a primary constructor -- serialized
12-
val name: String
13-
) {
14-
var stars: Int = 0 // property with getter & setter -- serialized
15-
16-
val path: String // getter only -- not serialized
17-
get() = "kotlin/$name"
18-
19-
private var locked: Boolean = false // private, not accessible -- not serialized
20-
}
21-
9+
// NOT @Serializable
10+
class Project(val name: String, val language: String)
11+
2212
@Serializer(forClass = Project::class)
2313
object ProjectSerializer
2414

2515
fun main() {
26-
val data = Project("kotlinx.serialization").apply { stars = 9000 }
27-
println(Json.encodeToString(ProjectSerializer, data))
16+
val data = Project("kotlinx.serialization", "Kotlin")
17+
println(Json.encodeToString(ProjectSerializer, data))
2818
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This file was automatically generated from serializers.md by Knit tool. Do not edit.
2+
package example.exampleSerializer23
3+
4+
import kotlinx.serialization.*
5+
import kotlinx.serialization.json.*
6+
import kotlinx.serialization.encoding.*
7+
import kotlinx.serialization.descriptors.*
8+
9+
// NOT @Serializable, will use external serializer
10+
class Project(
11+
// val in a primary constructor -- serialized
12+
val name: String
13+
) {
14+
var stars: Int = 0 // property with getter & setter -- serialized
15+
16+
val path: String // getter only -- not serialized
17+
get() = "kotlin/$name"
18+
19+
private var locked: Boolean = false // private, not accessible -- not serialized
20+
}
21+
22+
@Serializer(forClass = Project::class)
23+
object ProjectSerializer
24+
25+
fun main() {
26+
val data = Project("kotlinx.serialization").apply { stars = 9000 }
27+
println(Json.encodeToString(ProjectSerializer, data))
28+
}

0 commit comments

Comments
 (0)