From 27eb6971a5f58ea7c768393c25b41e21f5d6f24d Mon Sep 17 00:00:00 2001
From: "p.vezarko"
Date: Wed, 8 May 2024 12:55:05 +0300
Subject: [PATCH] JsonWriter and JsonReader for Instant, LocalDate,
LocalDateTime, OffsetDateTime, ZonedDateTime
---
.../main/scala/tethys/readers/KeyReader.scala | 24 +++++++++++++++
.../readers/instances/AllJsonReaders.scala | 10 +++++++
.../main/scala/tethys/writers/KeyWriter.scala | 15 ++++++++++
.../writers/instances/AllJsonWriters.scala | 30 +++++++++++++++++++
.../tethys/readers/DefaultReadersTest.scala | 21 ++++++++++++-
.../tethys/writers/DefaultWritersTest.scala | 21 ++++++++++++-
6 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/modules/core/src/main/scala/tethys/readers/KeyReader.scala b/modules/core/src/main/scala/tethys/readers/KeyReader.scala
index c4814ff8..49aed76f 100644
--- a/modules/core/src/main/scala/tethys/readers/KeyReader.scala
+++ b/modules/core/src/main/scala/tethys/readers/KeyReader.scala
@@ -20,4 +20,28 @@ object KeyReader {
implicit lazy val longKeyReader: KeyReader[Long] = new KeyReader[Long] {
override def read(s: String)(implicit fieldName: FieldName): Long = s.toLong
}
+
+ implicit lazy val instantKeyReader: KeyReader[java.time.Instant] = new KeyReader[java.time.Instant] {
+ override def read(s: String)(implicit fieldName: FieldName): java.time.Instant = java.time.Instant.parse(s)
+ }
+
+ implicit lazy val localDateKeyReader: KeyReader[java.time.LocalDate] = new KeyReader[java.time.LocalDate] {
+ override def read(s: String)(implicit fieldName: FieldName): java.time.LocalDate =
+ java.time.LocalDate.parse(s, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
+ }
+
+ implicit lazy val localDateTimeKeyReader: KeyReader[java.time.LocalDateTime] = new KeyReader[java.time.LocalDateTime] {
+ override def read(s: String)(implicit fieldName: FieldName): java.time.LocalDateTime =
+ java.time.LocalDateTime.parse(s, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
+ }
+
+ implicit lazy val offsetDateTimeKeyReader: KeyReader[java.time.OffsetDateTime] = new KeyReader[java.time.OffsetDateTime] {
+ override def read(s: String)(implicit fieldName: FieldName): java.time.OffsetDateTime =
+ java.time.OffsetDateTime.parse(s, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)
+ }
+
+ implicit lazy val zonedDateTimeKeyReader: KeyReader[java.time.ZonedDateTime] = new KeyReader[java.time.ZonedDateTime] {
+ override def read(s: String)(implicit fieldName: FieldName): java.time.ZonedDateTime =
+ java.time.ZonedDateTime.parse(s, java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME)
+ }
}
diff --git a/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala
index 19591382..c5e6bc89 100644
--- a/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala
+++ b/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala
@@ -136,4 +136,14 @@ trait AllJsonReaders extends OptionReaders {
implicit lazy val javaBigIntegerReader: JsonReader[java.math.BigInteger] = bigIntReader.map(_.bigInteger)
implicit lazy val javaUUIDReader: JsonReader[java.util.UUID] = stringReader.map(java.util.UUID.fromString(_))
+ implicit lazy val javaInstantReader: JsonReader[java.time.Instant] = stringReader.map(java.time.Instant.parse)
+ implicit lazy val javaLocalDateReader: JsonReader[java.time.LocalDate] =
+ stringReader.map(java.time.LocalDate.parse(_, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE))
+ implicit lazy val javaLocalDateTimeReader: JsonReader[java.time.LocalDateTime] =
+ stringReader.map(java.time.LocalDateTime.parse(_, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME))
+ implicit lazy val javaOffsetDateTimeReader: JsonReader[java.time.OffsetDateTime] =
+ stringReader.map(java.time.OffsetDateTime.parse(_, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME))
+ implicit lazy val javaZonedDateTimeReader: JsonReader[java.time.ZonedDateTime] =
+ stringReader.map(java.time.ZonedDateTime.parse(_, java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME))
+
}
diff --git a/modules/core/src/main/scala/tethys/writers/KeyWriter.scala b/modules/core/src/main/scala/tethys/writers/KeyWriter.scala
index f22acac1..c624e110 100644
--- a/modules/core/src/main/scala/tethys/writers/KeyWriter.scala
+++ b/modules/core/src/main/scala/tethys/writers/KeyWriter.scala
@@ -13,4 +13,19 @@ object KeyWriter {
implicit lazy val intKeyWriter: KeyWriter[Int] = _.toString
implicit lazy val longKeyWriter: KeyWriter[Long] = _.toString
+
+ implicit lazy val instantKeyWriter: KeyWriter[java.time.Instant] = _.toString
+
+ implicit lazy val localDateKeyWriter: KeyWriter[java.time.LocalDate] =
+ _.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
+
+ implicit lazy val localDateTimeKeyWriter: KeyWriter[java.time.LocalDateTime] =
+ _.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
+
+ implicit lazy val offsetDateTimeKeyWriter
+ : KeyWriter[java.time.OffsetDateTime] =
+ _.format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)
+
+ implicit lazy val zonedDateTimeKeyWriter: KeyWriter[java.time.ZonedDateTime] =
+ _.format(java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME)
}
diff --git a/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala b/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala
index d7683dd4..3407149b 100644
--- a/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala
+++ b/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala
@@ -79,4 +79,34 @@ trait AllJsonWriters extends OptionWriters with EitherWriters {
implicit lazy val nullWriter: JsonWriter[Null] = new JsonWriter[Null] {
override def write(value: Null, tokenWriter: TokenWriter): Unit = tokenWriter.writeNull()
}
+
+ implicit lazy val instantWriter: JsonWriter[java.time.Instant] =
+ new JsonWriter[java.time.Instant] {
+ override def write(value: java.time.Instant, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeString(value.toString)
+ }
+
+ implicit lazy val localDateWriter: JsonWriter[java.time.LocalDate] =
+ new JsonWriter[java.time.LocalDate] {
+ override def write(value: java.time.LocalDate, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeString(value.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE))
+ }
+
+ implicit lazy val localDateTimeWriter: JsonWriter[java.time.LocalDateTime] =
+ new JsonWriter[java.time.LocalDateTime] {
+ override def write(value: java.time.LocalDateTime, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeString(value.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME))
+ }
+
+ implicit lazy val offsetDateTimeWriter: JsonWriter[java.time.OffsetDateTime] =
+ new JsonWriter[java.time.OffsetDateTime] {
+ override def write(value: java.time.OffsetDateTime, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeString(value.format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME))
+ }
+
+ implicit lazy val zonedDateTimeWriter: JsonWriter[java.time.ZonedDateTime] =
+ new JsonWriter[java.time.ZonedDateTime] {
+ override def write(value: java.time.ZonedDateTime, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeString(value.format(java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME))
+ }
}
diff --git a/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala b/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala
index fe6f66f5..58225a75 100644
--- a/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala
+++ b/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala
@@ -13,6 +13,11 @@ import scala.reflect.ClassTag
class DefaultReadersTest extends AnyFlatSpec {
private val randomUUID = java.util.UUID.randomUUID()
+ private val instantNow = java.time.Instant.now()
+ private val localDateNow = java.time.LocalDate.now()
+ private val localDateTimeNow = java.time.LocalDateTime.now()
+ private val offsetDateTimeNow = java.time.OffsetDateTime.now()
+ private val zonedDateTimeNow = java.time.ZonedDateTime.now()
private def test[A](result: A)(implicit jsonReader: JsonReader[A], ct: ClassTag[A]): TestDefinition[A] = {
TestDefinition(result, jsonReader, ct.toString())
@@ -36,6 +41,15 @@ class DefaultReadersTest extends AnyFlatSpec {
test(List[Int](), "Seq.empty") -> arr(),
test(Map("a" -> 1, "b" -> 2)) -> obj("a" -> 1, "b" -> 2),
test(Map(randomUUID -> 1),"Map with UUID keys") -> obj(randomUUID.toString -> 1),
+ test(Map(instantNow -> 1), "Map with Instant keys") -> obj(instantNow.toString -> 1),
+ test(Map(localDateNow -> 1), "Map with LocalDate keys") ->
+ obj(localDateNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE) -> 1),
+ test(Map(localDateTimeNow -> 1), "Map with LocalDateTime keys") ->
+ obj(localDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME) -> 1),
+ test(Map(offsetDateTimeNow -> 1), "Map with OffsetDateTime keys") ->
+ obj(offsetDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME) -> 1),
+ test(Map(zonedDateTimeNow -> 1), "Map with ZonedDateTime keys") ->
+ obj(zonedDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME) -> 1),
test(Map(1L -> 1), "Map with Long keys") -> obj("1" -> 1),
test(Map(1 -> 1), "Map with Int keys") -> obj("1" -> 1),
test(Option(1), "Option.nonEmpty") -> value(1),
@@ -47,7 +61,12 @@ class DefaultReadersTest extends AnyFlatSpec {
test(1d: java.lang.Double) -> value(1d),
test(java.math.BigDecimal.valueOf(1)) -> value(1: BigDecimal),
test(java.math.BigInteger.valueOf(1)) -> value(1: BigInt),
- test(randomUUID) -> value(randomUUID.toString)
+ test(randomUUID) -> value(randomUUID.toString),
+ test(instantNow) -> value(instantNow.toString),
+ test(localDateNow) -> value(localDateNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)),
+ test(localDateTimeNow) -> value(localDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)),
+ test(offsetDateTimeNow) -> value(offsetDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)),
+ test(zonedDateTimeNow) -> value(zonedDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME))
)
behavior of "Default readers"
diff --git a/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala b/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala
index ffc26fb0..d674c299 100644
--- a/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala
+++ b/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala
@@ -12,6 +12,11 @@ import scala.reflect.ClassTag
class DefaultWritersTest extends AnyFlatSpec {
private val randomUUID = java.util.UUID.randomUUID()
+ private val instantNow = java.time.Instant.now()
+ private val localDateNow = java.time.LocalDate.now()
+ private val localDateTimeNow = java.time.LocalDateTime.now()
+ private val offsetDateTimeNow = java.time.OffsetDateTime.now()
+ private val zonedDateTimeNow = java.time.ZonedDateTime.now()
private def test[A](value: A)(implicit jsonWriter: JsonWriter[A], ct: ClassTag[A]): TestDefinition[A] = {
TestDefinition(value, jsonWriter, ct.toString())
@@ -38,6 +43,15 @@ class DefaultWritersTest extends AnyFlatSpec {
test(Map(randomUUID -> 1),"Map with UUID keys") -> obj(randomUUID.toString -> 1),
test(Map(1L -> 1), "Map with Long keys") -> obj("1" -> 1),
test(Map(1 -> 1), "Map with Int keys") -> obj("1" -> 1),
+ test(Map(instantNow -> 1), "Map with Instant keys") -> obj(instantNow.toString -> 1),
+ test(Map(localDateNow -> 1), "Map with LocalDate keys") ->
+ obj(localDateNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE) -> 1),
+ test(Map(localDateTimeNow -> 1), "Map with LocalDateTime keys") ->
+ obj(localDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME) -> 1),
+ test(Map(offsetDateTimeNow -> 1), "Map with OffsetDateTime keys") ->
+ obj(offsetDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME) -> 1),
+ test(Map(zonedDateTimeNow -> 1), "Map with ZonedDateTime keys") ->
+ obj(zonedDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME) -> 1),
test(Option(1), "Option.nonEmpty") -> value(1),
test(Option.empty[Int], "Option.empty") -> List(NullValueNode),
test(Right(1): Either[String, Int], "Either.right") -> value(1),
@@ -49,7 +63,12 @@ class DefaultWritersTest extends AnyFlatSpec {
test(1d: java.lang.Double) -> value(1d),
test(java.math.BigDecimal.valueOf(1)) -> value(1: BigDecimal),
test(java.math.BigInteger.valueOf(1)) -> value(1: BigInt),
- test(randomUUID) -> value(randomUUID.toString)
+ test(randomUUID) -> value(randomUUID.toString),
+ test(instantNow) -> value(instantNow.toString),
+ test(localDateNow) -> value(localDateNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)),
+ test(localDateTimeNow) -> value(localDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)),
+ test(offsetDateTimeNow) -> value(offsetDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)),
+ test(zonedDateTimeNow) -> value(zonedDateTimeNow.format(java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME))
)
behavior of "Default writers"