Skip to content

Commit 9020bda

Browse files
committed
Add the name parameter to custom serializers
1 parent b03784e commit 9020bda

10 files changed

+85
-28
lines changed

core/common/src/serializers/InstantSerializers.kt

+17-5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
package kotlinx.datetime.serializers
77

8-
import kotlinx.datetime.Instant
9-
import kotlinx.datetime.format
8+
import kotlinx.datetime.*
109
import kotlinx.datetime.format.DateTimeComponents
1110
import kotlinx.datetime.format.DateTimeFormat
1211
import kotlinx.serialization.*
@@ -86,30 +85,43 @@ public object InstantComponentSerializer : KSerializer<Instant> {
8685
* [format] should be a format that includes enough components to unambiguously define a date, a time, and a UTC offset.
8786
* See [Instant.parse] for details of how deserialization is performed.
8887
*
88+
* [name] is the name of the serializer.
89+
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.Instant serializer `[name].
90+
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
91+
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
92+
* to avoid conflicts with serializers defined by other libraries and client code.
93+
*
8994
* When serializing, the [Instant] value is formatted as a string using the specified [format]
9095
* in the [ZERO][UtcOffset.ZERO] UTC offset.
9196
*
9297
* This serializer is abstract and must be subclassed to provide a concrete serializer.
9398
* Example:
9499
* ```
95-
* object Rfc1123InstantSerializer : FormattedInstantSerializer(DateTimeComponents.Formats.RFC_1123)
100+
* // serializes LocalDateTime(2008, 6, 30, 11, 5, 30).toInstant(TimeZone.UTC)
101+
* // as the string "Mon, 30 Jun 2008 11:05:30 GMT"
102+
* object Rfc1123InstantSerializer : FormattedInstantSerializer(
103+
* "my.package.RFC1123", DateTimeComponents.Formats.RFC_1123
104+
* )
96105
* ```
97106
*
98107
* Note that [Instant] is [kotlinx.serialization.Serializable] by default,
99108
* so it is not necessary to create custom serializers when the format is not important.
100109
* Additionally, [InstantIso8601Serializer] is provided for the ISO 8601 format.
101110
*/
102111
public abstract class FormattedInstantSerializer(
112+
name: String,
103113
private val format: DateTimeFormat<DateTimeComponents>,
104114
) : KSerializer<Instant> {
105-
106115
override val descriptor: SerialDescriptor =
107-
PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING)
116+
PrimitiveSerialDescriptor("kotlinx.datetime.Instant serializer $name", PrimitiveKind.STRING)
108117

109118
override fun deserialize(decoder: Decoder): Instant =
110119
Instant.parse(decoder.decodeString(), format)
111120

112121
override fun serialize(encoder: Encoder, value: Instant) {
113122
encoder.encodeString(value.format(format))
114123
}
124+
125+
@OptIn(ExperimentalSerializationApi::class)
126+
override fun toString(): String = descriptor.serialName
115127
}

core/common/src/serializers/LocalDateSerializers.kt

+15-5
Original file line numberDiff line numberDiff line change
@@ -82,28 +82,38 @@ public object LocalDateComponentSerializer: KSerializer<LocalDate> {
8282
* An abstract serializer for [LocalDate] values that uses
8383
* a custom [DateTimeFormat] to serialize and deserialize the value.
8484
*
85+
* [name] is the name of the serializer.
86+
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.LocalDate serializer `[name].
87+
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
88+
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
89+
* to avoid conflicts with serializers defined by other libraries and client code.
90+
*
8591
* This serializer is abstract and must be subclassed to provide a concrete serializer.
8692
* Example:
8793
* ```
88-
* object IsoBasicLocalDateSerializer : FormattedLocalDateSerializer(LocalDate.Formats.ISO_BASIC)
94+
* // serializes LocalDate(2020, 1, 4) as the string "20200104"
95+
* object IsoBasicLocalDateSerializer :
96+
* FormattedLocalDateSerializer("my.package.ISO_BASIC", LocalDate.Formats.ISO_BASIC)
8997
* ```
9098
*
9199
* Note that [LocalDate] is [kotlinx.serialization.Serializable] by default,
92100
* so it is not necessary to create custom serializers when the format is not important.
93101
* Additionally, [LocalDateIso8601Serializer] is provided for the ISO 8601 format.
94102
*/
95103
public abstract class FormattedLocalDateSerializer(
96-
format: DateTimeFormat<LocalDate>,
97-
) : KSerializer<LocalDate> by format.asKSerializer("kotlinx.datetime.LocalDate")
104+
name: String, format: DateTimeFormat<LocalDate>
105+
) : KSerializer<LocalDate> by format.asKSerializer("kotlinx.datetime.LocalDate serializer $name")
98106

99-
internal fun <T> DateTimeFormat<T>.asKSerializer(classFqn: String): KSerializer<T> =
107+
internal fun <T> DateTimeFormat<T>.asKSerializer(serialName: String): KSerializer<T> =
100108
object : KSerializer<T> {
101109
override val descriptor: SerialDescriptor =
102-
PrimitiveSerialDescriptor(classFqn, PrimitiveKind.STRING)
110+
PrimitiveSerialDescriptor(serialName, PrimitiveKind.STRING)
103111

104112
override fun deserialize(decoder: Decoder): T = parse(decoder.decodeString())
105113

106114
override fun serialize(encoder: Encoder, value: T) {
107115
encoder.encodeString(format(value))
108116
}
117+
118+
override fun toString(): String = serialName
109119
}

core/common/src/serializers/LocalDateTimeSerializers.kt

+16-7
Original file line numberDiff line numberDiff line change
@@ -104,20 +104,29 @@ public object LocalDateTimeComponentSerializer: KSerializer<LocalDateTime> {
104104
* An abstract serializer for [LocalDateTime] values that uses
105105
* a custom [DateTimeFormat] to serialize and deserialize the value.
106106
*
107+
* [name] is the name of the serializer.
108+
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.LocalDateTime serializer `[name].
109+
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
110+
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
111+
* to avoid conflicts with serializers defined by other libraries and client code.
112+
*
107113
* This serializer is abstract and must be subclassed to provide a concrete serializer.
108114
* Example:
109115
* ```
110-
* object PythonDateTimeSerializer : FormattedLocalDateTimeSerializer(LocalDateTime.Format {
111-
* date(LocalDate.Formats.ISO)
112-
* char(' ')
113-
* time(LocalTime.Formats.ISO)
114-
* })
116+
* // serializes LocalDateTime(2020, 1, 4, 12, 30) as the string "2020-01-04 12:30"
117+
* object PythonDateTimeSerializer : FormattedLocalDateTimeSerializer("my.package.PythonDateTime",
118+
* LocalDateTime.Format {
119+
* date(LocalDate.Formats.ISO)
120+
* char(' ')
121+
* time(LocalTime.Formats.ISO)
122+
* }
123+
* )
115124
* ```
116125
*
117126
* Note that [LocalDateTime] is [kotlinx.serialization.Serializable] by default,
118127
* so it is not necessary to create custom serializers when the format is not important.
119128
* Additionally, [LocalDateTimeIso8601Serializer] is provided for the ISO 8601 format.
120129
*/
121130
public abstract class FormattedLocalDateTimeSerializer(
122-
format: DateTimeFormat<LocalDateTime>,
123-
) : KSerializer<LocalDateTime> by format.asKSerializer("kotlinx.datetime.LocalDateTime")
131+
name: String, format: DateTimeFormat<LocalDateTime>
132+
) : KSerializer<LocalDateTime> by format.asKSerializer("kotlinx.datetime.LocalDateTime serializer $name")

core/common/src/serializers/LocalTimeSerializers.kt

+10-3
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,17 @@ public object LocalTimeComponentSerializer : KSerializer<LocalTime> {
8787
* An abstract serializer for [LocalTime] values that uses
8888
* a custom [DateTimeFormat] to serialize and deserialize the value.
8989
*
90+
* [name] is the name of the serializer.
91+
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.LocalTime serializer `[name].
92+
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
93+
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
94+
* to avoid conflicts with serializers defined by other libraries and client code.
95+
*
9096
* This serializer is abstract and must be subclassed to provide a concrete serializer.
9197
* Example:
9298
* ```
93-
* object FixedWidthTimeSerializer : FormattedLocalTimeSerializer(LocalTime.Format {
99+
* // serializes LocalTime(12, 30) as "12:30:00.000"
100+
* object FixedWidthTimeSerializer : FormattedLocalTimeSerializer("my.package.FixedWidthTime", LocalTime.Format {
94101
* hour(); char(':'); minute(); char(':'); second(); char('.'); secondFraction(3)
95102
* })
96103
* ```
@@ -100,5 +107,5 @@ public object LocalTimeComponentSerializer : KSerializer<LocalTime> {
100107
* Additionally, [LocalTimeIso8601Serializer] is provided for the ISO 8601 format.
101108
*/
102109
public abstract class FormattedLocalTimeSerializer(
103-
format: DateTimeFormat<LocalTime>,
104-
) : KSerializer<LocalTime> by format.asKSerializer("kotlinx.datetime.LocalTime")
110+
name: String, format: DateTimeFormat<LocalTime>
111+
) : KSerializer<LocalTime> by format.asKSerializer("kotlinx.datetime.LocalTime serializer $name")

core/common/src/serializers/TimeZoneSerializers.kt

+12-3
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,25 @@ public object UtcOffsetSerializer: KSerializer<UtcOffset> {
7878
* An abstract serializer for [UtcOffset] values that uses
7979
* a custom [DateTimeFormat] to serialize and deserialize the value.
8080
*
81+
* [name] is the name of the serializer.
82+
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.UtcOffset serializer `[name].
83+
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
84+
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
85+
* to avoid conflicts with serializers defined by other libraries and client code.
86+
*
8187
* This serializer is abstract and must be subclassed to provide a concrete serializer.
8288
* Example:
8389
* ```
84-
* object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer(UtcOffset.Formats.FOUR_DIGITS)
90+
* // serializes the UTC offset UtcOffset(hours = 2) as the string "+0200"
91+
* object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer(
92+
* "my.package.FOUR_DIGITS", UtcOffset.Formats.FOUR_DIGITS
93+
* )
8594
* ```
8695
*
8796
* Note that [UtcOffset] is [kotlinx.serialization.Serializable] by default,
8897
* so it is not necessary to create custom serializers when the format is not important.
8998
* Additionally, [UtcOffsetSerializer] is provided for the ISO 8601 format.
9099
*/
91100
public abstract class FormattedUtcOffsetSerializer(
92-
format: DateTimeFormat<UtcOffset>,
93-
) : KSerializer<UtcOffset> by format.asKSerializer("kotlinx.datetime.UtcOffset")
101+
name: String, format: DateTimeFormat<UtcOffset>
102+
) : KSerializer<UtcOffset> by format.asKSerializer("kotlinx.datetime.UtcOffset serializer $name")

serialization/common/test/InstantSerializationTest.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ class InstantSerializationTest {
6868
iso8601Serialization(Json.serializersModule.serializer())
6969
}
7070

71-
object Rfc1123InstantSerializer : FormattedInstantSerializer(DateTimeComponents.Formats.RFC_1123)
71+
object Rfc1123InstantSerializer : FormattedInstantSerializer("RFC_1123", DateTimeComponents.Formats.RFC_1123)
7272

7373
@Test
7474
fun testCustomSerializer() {
75+
assertKSerializerName("kotlinx.datetime.Instant serializer RFC_1123", Rfc1123InstantSerializer)
7576
for ((instant, json) in listOf(
7677
Pair(Instant.fromEpochSeconds(1607505416),
7778
"\"Wed, 9 Dec 2020 09:16:56 GMT\""),

serialization/common/test/LocalDateSerializationTest.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ class LocalDateSerializationTest {
7070
iso8601Serialization(Json.serializersModule.serializer())
7171
}
7272

73-
object IsoBasicLocalDateSerializer : FormattedLocalDateSerializer(LocalDate.Formats.ISO_BASIC)
73+
object IsoBasicLocalDateSerializer : FormattedLocalDateSerializer("ISO_BASIC", LocalDate.Formats.ISO_BASIC)
7474

7575
@Test
7676
fun testCustomSerializer() {
77+
assertKSerializerName("kotlinx.datetime.LocalDate serializer ISO_BASIC", IsoBasicLocalDateSerializer)
7778
for ((localDate, json) in listOf(
7879
Pair(LocalDate(2020, 12, 9), "\"20201209\""),
7980
Pair(LocalDate(-2020, 1, 1), "\"-20200101\""),
@@ -84,3 +85,8 @@ class LocalDateSerializationTest {
8485
}
8586
}
8687
}
88+
89+
@OptIn(ExperimentalSerializationApi::class)
90+
fun <T> assertKSerializerName(expectedName: String, serializer: KSerializer<T>) {
91+
assertEquals(expectedName, serializer.descriptor.serialName)
92+
}

serialization/common/test/LocalDateTimeSerializationTest.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,15 @@ class LocalDateTimeSerializationTest {
8484
iso8601Serialization(Json.serializersModule.serializer())
8585
}
8686

87-
object PythonDateTimeSerializer : FormattedLocalDateTimeSerializer(LocalDateTime.Format {
87+
object PythonDateTimeSerializer : FormattedLocalDateTimeSerializer("PythonDateTime", LocalDateTime.Format {
8888
date(LocalDate.Formats.ISO)
8989
char(' ')
9090
time(LocalTime.Formats.ISO)
9191
})
9292

9393
@Test
9494
fun testCustomSerializer() {
95+
assertKSerializerName("kotlinx.datetime.LocalDateTime serializer PythonDateTime", PythonDateTimeSerializer)
9596
for ((localDateTime, json) in listOf(
9697
Pair(LocalDateTime(2008, 7, 5, 2, 1), "\"2008-07-05 02:01:00\""),
9798
Pair(LocalDateTime(2007, 12, 31, 23, 59, 1), "\"2007-12-31 23:59:01\""),

serialization/common/test/LocalTimeSerializationTest.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,13 @@ class LocalTimeSerializationTest {
7474
iso8601Serialization(Json.serializersModule.serializer())
7575
}
7676

77-
object FixedWidthTimeSerializer : FormattedLocalTimeSerializer(LocalTime.Format {
77+
object FixedWidthTimeSerializer : FormattedLocalTimeSerializer("FixedWidth", LocalTime.Format {
7878
hour(); char(':'); minute(); char(':'); second(); char('.'); secondFraction(3)
7979
})
8080

8181
@Test
8282
fun testCustomSerializer() {
83+
assertKSerializerName("kotlinx.datetime.LocalTime serializer FixedWidth", FixedWidthTimeSerializer)
8384
for ((localTime, json) in listOf(
8485
Pair(LocalTime(2, 1), "\"02:01:00.000\""),
8586
Pair(LocalTime(23, 59, 1), "\"23:59:01.000\""),

serialization/common/test/UtcOffsetSerializationTest.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ class UtcOffsetSerializationTest {
3636
testSerializationAsPrimitive(UtcOffset.serializer())
3737
}
3838

39-
object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer(UtcOffset.Formats.FOUR_DIGITS)
39+
object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer("FOUR_DIGITS", UtcOffset.Formats.FOUR_DIGITS)
4040

4141
@Test
4242
fun testCustomSerializer() {
43+
assertKSerializerName("kotlinx.datetime.UtcOffset serializer FOUR_DIGITS", FourDigitOffsetSerializer)
4344
for ((utcOffset, json) in listOf(
4445
Pair(UtcOffset.ZERO, "\"+0000\""),
4546
Pair(UtcOffset(2), "\"+0200\""),

0 commit comments

Comments
 (0)