All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.shiftforward.apso.json.ExtraJsonProtocol.scala Maven / Gradle / Ivy

There is a newer version: 0.13.11
Show newest version
package eu.shiftforward.apso.json

import java.net.URI

import scala.concurrent.duration._
import scala.util.{ Failure, Success, Try }

import com.github.nscala_time.time.Imports._
import com.typesafe.config.{ Config, ConfigFactory, ConfigRenderOptions }
import spray.json.DefaultJsonProtocol._
import spray.json._

import eu.shiftforward.apso.config.Implicits._

/**
 * Provides additional JsonFormats not available in the [[spray.json.DefaultJsonProtocol]].
 */
object ExtraJsonProtocol
  extends ExtraTimeJsonProtocol
  with ExtraHttpJsonProtocol
  with ExtraMiscJsonProtocol

trait ExtraTimeJsonProtocol {
  implicit object FiniteDurationJsonFormat extends JsonFormat[FiniteDuration] {
    def write(dur: FiniteDuration) = JsObject("milliseconds" -> dur.toMillis.toJson)

    def read(json: JsValue) = {

      def tryToParseDuration(duration: String) =
        Try(ConfigFactory.parseString(s"d=$duration").get[FiniteDuration]("d")) match {
          case Success(d) => d
          case Failure(t) => deserializationError("Expected a Number or a unit-annotated String", t)
        }

      json match {
        case JsNumber(duration) =>
          tryToParseDuration(duration.toString())

        case JsString(duration) =>
          tryToParseDuration(duration)

        case j: JsObject =>
          j.fields.headOption match {
            case Some(("milliseconds", JsNumber(milliseconds))) => milliseconds.longValue.millis
            case Some(("seconds", JsNumber(seconds))) => seconds.longValue.seconds
            case Some(("minutes", JsNumber(minutes))) => minutes.longValue.minutes
            case Some(("hours", JsNumber(hours))) => hours.longValue.hours
            case Some(("days", JsNumber(days))) => days.longValue.days
            case _ => deserializationError(
              "Expected the following units: 'milliseconds', 'seconds', 'minutes', 'hours' or 'days'.")
          }
        case _ => deserializationError("Expected either a Number, String or JSON Object!")
      }
    }
  }

  implicit object IntervalJsonFormat extends JsonFormat[Interval] {
    def write(i: Interval): JsValue =
      JsObject(
        "startMillis" -> i.getStartMillis.toJson,
        "endMillis" -> i.getEndMillis.toJson)

    def read(json: JsValue): Interval = {
      json.asJsObject.getFields("startMillis", "endMillis") match {
        case Seq(startMillis, endMillis) =>
          new Interval(startMillis.convertTo[Long], endMillis.convertTo[Long])
        case _ =>
          deserializationError(
            "One ore more fields are missing or malformed in the Interval Json. " +
              "Required fields: 'startMillis' and 'endMillis'.")
      }
    }
  }
}

trait ExtraHttpJsonProtocol {

  implicit object URIFormat extends JsonFormat[URI] {
    def write(uri: URI) = JsString(uri.toString)

    def read(json: JsValue) = json match {
      case JsString(uri) =>
        Try(new URI(uri)).getOrElse(deserializationError("Invalid URI: " + uri))
      case other => deserializationError("Expected String with URI, got: " + other)
    }
  }
}

trait ExtraMiscJsonProtocol {
  implicit object ConfigJsonFormat extends JsonFormat[Config] {
    def write(conf: Config): JsValue = conf.root.render(ConfigRenderOptions.concise()).parseJson
    def read(json: JsValue): Config = Try(ConfigFactory.parseString(json.toString)) match {
      case Success(v) => v
      case Failure(t) => deserializationError("Could not parse config: " + json, t)
    }
  }

  implicit object DateTimeFormat extends JsonFormat[DateTime] {
    override def write(date: DateTime): JsValue = JsString(date.toString)

    override def read(json: JsValue): DateTime = json match {
      case JsString(date) => new DateTime(date)
      case _ =>
        deserializationError("The value for a 'DateTime' has an invalid type - it must be a String.")
    }
  }

  implicit object LocalDateFormat extends JsonFormat[LocalDate] {
    override def write(date: LocalDate): JsValue = date.toString.toJson

    override def read(json: JsValue): LocalDate = json match {
      case JsString(date) => date.toLocalDate
      case _ =>
        deserializationError("The value for a 'LocalDate' has an invalid type - it must be a String.")
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy