aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/core/json.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/core/json.scala')
-rw-r--r--src/main/scala/xyz/driver/core/json.scala57
1 files changed, 50 insertions, 7 deletions
diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala
index 639af22..98725fb 100644
--- a/src/main/scala/xyz/driver/core/json.scala
+++ b/src/main/scala/xyz/driver/core/json.scala
@@ -1,6 +1,8 @@
package xyz.driver.core
import java.net.InetAddress
+import java.time.format.DateTimeFormatter
+import java.time.{Instant, LocalDate}
import java.util.{TimeZone, UUID}
import akka.http.scaladsl.marshalling.{Marshaller, Marshalling}
@@ -23,6 +25,7 @@ import xyz.driver.core.time.{Time, TimeOfDay}
import scala.reflect.{ClassTag, classTag}
import scala.reflect.runtime.universe._
import scala.util.Try
+import scala.util.control.NonFatal
object json {
import DefaultJsonProtocol._
@@ -77,25 +80,49 @@ object json {
}
}
- def TimeInPath: PathMatcher1[Time] =
+ def TimeInPath: PathMatcher1[Time] = InstantInPath.map(instant => Time(instant.toEpochMilli))
+
+ private def timestampInPath: PathMatcher1[Long] =
PathMatcher("""[+-]?\d*""".r) flatMap { string =>
- try Some(Time(string.toLong))
+ try Some(string.toLong)
catch { case _: IllegalArgumentException => None }
}
- implicit val timeFormat = new RootJsonFormat[Time] {
+ def InstantInPath: PathMatcher1[Instant] =
+ new PathMatcher1[Instant] {
+ def apply(path: Path): PathMatcher.Matching[Tuple1[Instant]] = path match {
+ case Path.Segment(head, tail) =>
+ try Matched(tail, Tuple1(Instant.parse(head)))
+ catch {
+ case NonFatal(_) => Unmatched
+ }
+ case _ => Unmatched
+ }
+ } | timestampInPath.map(Instant.ofEpochMilli)
+
+ implicit val timeFormat: RootJsonFormat[Time] = new RootJsonFormat[Time] {
def write(time: Time) = JsObject("timestamp" -> JsNumber(time.millis))
- def read(value: JsValue): Time = value match {
+ def read(value: JsValue): Time = Time(instantFormat.read(value))
+ }
+
+ implicit val instantFormat: JsonFormat[Instant] = new JsonFormat[Instant] {
+ def write(instant: Instant): JsValue = JsString(instant.toString)
+
+ def read(value: JsValue): Instant = value match {
case JsObject(fields) =>
fields
.get("timestamp")
.flatMap {
- case JsNumber(millis) => Some(Time(millis.toLong))
+ case JsNumber(millis) => Some(Instant.ofEpochMilli(millis.longValue()))
case _ => None
}
- .getOrElse(throw DeserializationException("Time expects number"))
- case _ => throw DeserializationException("Time expects number")
+ .getOrElse(deserializationError(s"Instant expects ISO timestamp but got ${value.compactPrint}"))
+ case JsNumber(millis) => Instant.ofEpochMilli(millis.longValue())
+ case JsString(str) =>
+ try Instant.parse(str)
+ catch { case NonFatal(_) => deserializationError(s"Instant expects ISO timestamp but got $str") }
+ case _ => deserializationError(s"Instant expects ISO timestamp but got ${value.compactPrint}")
}
}
@@ -140,6 +167,22 @@ object json {
}
}
+ implicit val localDateFormat = new RootJsonFormat[LocalDate] {
+ val format = DateTimeFormatter.ISO_LOCAL_DATE
+
+ def write(date: LocalDate): JsValue = JsString(date.format(format))
+ def read(value: JsValue): LocalDate = value match {
+ case JsString(dateString) =>
+ try LocalDate.parse(dateString, format)
+ catch {
+ case NonFatal(_) =>
+ throw deserializationError(s"Malformed ISO 8601 Date. Expected YYYY-MM-DD, but got $dateString.")
+ }
+ case _ =>
+ throw deserializationError(s"Malformed ISO 8601 Date. Expected YYYY-MM-DD, but got ${value.compactPrint}.")
+ }
+ }
+
implicit val monthFormat = new RootJsonFormat[Month] {
def write(month: Month) = JsNumber(month)
def read(value: JsValue): Month = value match {