package play.api.libs.json
import java.time.{
Instant,
LocalDate,
LocalDateTime,
ZoneId,
ZonedDateTime
}
import java.time.temporal.Temporal
import java.time.format.DateTimeFormatter
import play.api.libs.json.jackson.JacksonJson
import scala.annotation.implicitNotFound
import scala.collection._
import scala.reflect.ClassTag
import com.fasterxml.jackson.databind.JsonNode
import Json._
@implicitNotFound(
"No Json serializer found for type ${A}. Try to implement an implicit Writes or Format for this type."
)
trait Writes[-A] {
def writes(o: A): JsValue
def transform(transformer: JsValue => JsValue): Writes[A] = Writes[A] { a => transformer(this.writes(a)) }
def transform(transformer: Writes[JsValue]): Writes[A] = Writes[A] { a => transformer.writes(this.writes(a)) }
}
@implicitNotFound(
"No Json serializer as JsObject found for type ${A}. Try to implement an implicit OWrites or OFormat for this type."
)
trait OWrites[-A] extends Writes[A] {
def writes(o: A): JsObject
}
object OWrites extends PathWrites with ConstraintWrites {
import play.api.libs.functional._
implicit val functionalCanBuildOWrites: FunctionalCanBuild[OWrites] = new FunctionalCanBuild[OWrites] {
def apply[A, B](wa: OWrites[A], wb: OWrites[B]): OWrites[A ~ B] = OWrites[A ~ B] { case a ~ b => wa.writes(a).deepMerge(wb.writes(b)) }
}
implicit val contravariantfunctorOWrites: ContravariantFunctor[OWrites] = new ContravariantFunctor[OWrites] {
def contramap[A, B](wa: OWrites[A], f: B => A): OWrites[B] = OWrites[B](b => wa.writes(f(b)))
}
def apply[A](f: A => JsObject): OWrites[A] = new OWrites[A] {
def writes(a: A): JsObject = f(a)
}
}
object Writes extends PathWrites with ConstraintWrites with DefaultWrites {
val constraints: ConstraintWrites = this
val path: PathWrites = this
def apply[A](f: A => JsValue): Writes[A] = new Writes[A] {
def writes(a: A): JsValue = f(a)
}
}
trait DefaultWrites {
import scala.language.implicitConversions
implicit object IntWrites extends Writes[Int] {
def writes(o: Int) = JsNumber(o)
}
implicit object ShortWrites extends Writes[Short] {
def writes(o: Short) = JsNumber(o)
}
implicit object ByteWrites extends Writes[Byte] {
def writes(o: Byte) = JsNumber(o)
}
implicit object LongWrites extends Writes[Long] {
def writes(o: Long) = JsNumber(o)
}
implicit object FloatWrites extends Writes[Float] {
def writes(o: Float) = JsNumber(o)
}
implicit object DoubleWrites extends Writes[Double] {
def writes(o: Double) = JsNumber(o)
}
implicit object BigDecimalWrites extends Writes[BigDecimal] {
def writes(o: BigDecimal) = JsNumber(o)
}
implicit object BooleanWrites extends Writes[Boolean] {
def writes(o: Boolean) = JsBoolean(o)
}
implicit object StringWrites extends Writes[String] {
def writes(o: String) = JsString(o)
}
implicit object JsonNodeWrites extends Writes[JsonNode] {
def writes(o: JsonNode): JsValue = JacksonJson.jsonNodeToJsValue(o)
}
implicit def arrayWrites[T: ClassTag](implicit fmt: Writes[T]): Writes[Array[T]] = new Writes[Array[T]] {
def writes(ts: Array[T]) = JsArray((ts.map(t => toJson(t)(fmt))).toList)
}
implicit def mapWrites[V](implicit fmtv: Writes[V]): OWrites[collection.immutable.Map[String, V]] = OWrites[collection.immutable.Map[String, V]] { ts =>
JsObject(ts.map { case (k, v) => (k, toJson(v)(fmtv)) }.toList)
}
implicit def traversableWrites[A: Writes] = new Writes[Traversable[A]] {
def writes(as: Traversable[A]) = JsArray(as.map(toJson(_)).toSeq)
}
implicit object JsValueWrites extends Writes[JsValue] {
def writes(o: JsValue) = o
}
implicit def OptionWrites[T](implicit fmt: Writes[T]): Writes[Option[T]] = new Writes[Option[T]] {
def writes(o: Option[T]) = o match {
case Some(value) => fmt.writes(value)
case None => JsNull
}
}
def dateWrites(pattern: String): Writes[java.util.Date] = new Writes[java.util.Date] {
def writes(d: java.util.Date): JsValue = JsString(new java.text.SimpleDateFormat(pattern).format(d))
}
implicit object DefaultDateWrites extends Writes[java.util.Date] {
def writes(d: java.util.Date): JsValue = JsNumber(d.getTime)
}
trait TemporalFormatter[T <: Temporal] {
def format(temporal: T): String
}
object TemporalFormatter {
implicit def DefaultLocalDateTimeFormatter(formatter: DateTimeFormatter): TemporalFormatter[LocalDateTime] = new TemporalFormatter[LocalDateTime] {
def format(temporal: LocalDateTime): String = formatter.format(temporal)
}
implicit def PatternLocalDateTimeFormatter(pattern: String): TemporalFormatter[LocalDateTime] = new TemporalFormatter[LocalDateTime] {
def format(temporal: LocalDateTime): String =
DateTimeFormatter.ofPattern(pattern).format(temporal)
}
implicit def DefaultZonedDateTimeFormatter(formatter: DateTimeFormatter): TemporalFormatter[ZonedDateTime] = new TemporalFormatter[ZonedDateTime] {
def format(temporal: ZonedDateTime): String = formatter.format(temporal)
}
implicit def PatternZonedDateTimeFormatter(pattern: String): TemporalFormatter[ZonedDateTime] = new TemporalFormatter[ZonedDateTime] {
def format(temporal: ZonedDateTime): String =
DateTimeFormatter.ofPattern(pattern).format(temporal)
}
implicit def DefaultDateFormatter(formatter: DateTimeFormatter): TemporalFormatter[LocalDate] = new TemporalFormatter[LocalDate] {
def format(temporal: LocalDate): String = formatter.format(temporal)
}
implicit def PatternDateFormatter(pattern: String): TemporalFormatter[LocalDate] = new TemporalFormatter[LocalDate] {
def format(temporal: LocalDate): String =
DateTimeFormatter.ofPattern(pattern).format(temporal)
}
implicit def DefaultInstantFormatter(formatter: DateTimeFormatter): TemporalFormatter[Instant] = new TemporalFormatter[Instant] {
def format(temporal: Instant): String =
formatter format LocalDateTime.ofInstant(temporal, ZoneId.systemDefault)
}
implicit def PatternInstantFormatter(pattern: String): TemporalFormatter[Instant] = new TemporalFormatter[Instant] {
def format(temporal: Instant): String =
DateTimeFormatter.ofPattern(pattern).
format(LocalDateTime.ofInstant(temporal, ZoneId.systemDefault))
}
}
def temporalWrites[A <: Temporal, B](formatting: B)(implicit f: B => TemporalFormatter[A]): Writes[A] = new Writes[A] {
def writes(temporal: A): JsValue = JsString(f(formatting) format temporal)
}
implicit val DefaultLocalDateTimeWrites =
temporalWrites[LocalDateTime, DateTimeFormatter](
DateTimeFormatter.ISO_LOCAL_DATE_TIME)
implicit val DefaultZonedDateTimeWrites =
temporalWrites[ZonedDateTime, DateTimeFormatter](
DateTimeFormatter.ISO_LOCAL_DATE_TIME)
implicit val DefaultLocalDateWrites =
temporalWrites[LocalDate, DateTimeFormatter](
DateTimeFormatter.ISO_LOCAL_DATE)
implicit val DefaultInstantWrites =
temporalWrites[Instant, DateTimeFormatter](
DateTimeFormatter.ISO_LOCAL_DATE_TIME)
val : Writes[LocalDateTime] =
new [LocalDateTime] {
lazy val =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
def (: LocalDateTime): JsValue = JsNumber(BigDecimal.valueOf(
Instant.parse(formatter format t).toEpochMilli))
}
val : Writes[ZonedDateTime] =
new [ZonedDateTime] {
def (: ZonedDateTime): JsValue =
JsNumber(BigDecimal valueOf t.toInstant.toEpochMilli)
}
val LocalDateNumberWrites: Writes[LocalDate] = new Writes[LocalDate] {
lazy val formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'00:00:00'Z'")
def writes(t: LocalDate): JsValue = JsNumber(BigDecimal.valueOf(
Instant.parse(formatter format t).toEpochMilli))
}
val InstantNumberWrites: Writes[Instant] = new Writes[Instant] {
def writes(t: Instant): JsValue =
JsNumber(BigDecimal valueOf t.toEpochMilli)
}
def jodaDateWrites(pattern: String): Writes[org.joda.time.DateTime] = new Writes[org.joda.time.DateTime] {
val df = org.joda.time.format.DateTimeFormat.forPattern(pattern)
def writes(d: org.joda.time.DateTime): JsValue = JsString(d.toString(df))
}
implicit object DefaultJodaDateWrites extends Writes[org.joda.time.DateTime] {
def writes(d: org.joda.time.DateTime): JsValue = JsNumber(d.getMillis)
}
def jodaLocalDateWrites(pattern: String): Writes[org.joda.time.LocalDate] = new Writes[org.joda.time.LocalDate] {
val df = org.joda.time.format.DateTimeFormat.forPattern(pattern)
def writes(d: org.joda.time.LocalDate): JsValue = JsString(d.toString(df))
}
implicit object DefaultJodaLocalDateWrites extends Writes[org.joda.time.LocalDate] {
def writes(d: org.joda.time.LocalDate): JsValue = JsString(d.toString)
}
def jodaLocalTimeWrites(pattern: String): Writes[org.joda.time.LocalTime] = new Writes[org.joda.time.LocalTime] {
def writes(d: org.joda.time.LocalTime): JsValue = JsString(d.toString(pattern))
}
implicit object DefaultJodaLocalTimeWrites extends Writes[org.joda.time.LocalTime] {
def writes(d: org.joda.time.LocalTime): JsValue = JsString(d.toString)
}
def sqlDateWrites(pattern: String): Writes[java.sql.Date] = new Writes[java.sql.Date] {
def writes(d: java.sql.Date): JsValue = JsString(new java.text.SimpleDateFormat(pattern).format(d))
}
implicit object UuidWrites extends Writes[java.util.UUID] {
def writes(u: java.util.UUID) = JsString(u.toString())
}
implicit def enumNameWrites[E <: Enumeration]: Writes[E#Value] = new Writes[E#Value] {
def writes(value: E#Value): JsValue = JsString(value.toString)
}
private[json] object anyWrites extends Writes[Any] {
def writes(a: Any): JsValue = a match {
case s: String => JsString(s)
case nb: Int => JsNumber(nb)
case nb: Short => JsNumber(nb)
case nb: Long => JsNumber(nb)
case nb: Double => JsNumber(nb)
case nb: Float => JsNumber(nb)
case b: Boolean => JsBoolean(b)
case js: JsValue => js
case x => JsString(x.toString)
}
}
}