play.api.libs.json.Writes.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc.
*/
package play.api.libs.json
import play.api.libs.functional.ContravariantFunctor
import java.util.Date
import scala.annotation.implicitNotFound
import scala.collection._
import scala.reflect.ClassTag
/**
* Json serializer: write an implicit to define a serializer for any type
*/
@implicitNotFound(
"No Json serializer found for type ${A}. Try to implement an implicit Writes or Format for this type."
)
trait Writes[A] { self =>
/**
* Converts the `A` value into a [[JsValue]].
*/
def writes(o: A): JsValue
/**
* Returns a new instance that first converts a `B` value to a `A` one,
* before converting this `A` value into a [[JsValue]].
*/
def contramap[B](f: B => A): Writes[B] = Writes[B](b => self.writes(f(b)))
/**
* Narrows to any `B` super-type of `A`.
*/
def narrow[B <: A]: Writes[B] = this.asInstanceOf[Writes[B]]
/**
* Transforms the resulting [[JsValue]] using transformer function.
*/
def transform(transformer: JsValue => JsValue): Writes[A] = Writes[A] { a =>
transformer(self.writes(a))
}
/**
* Transforms the resulting [[JsValue]] using a `Writes[JsValue]`.
*/
def transform(transformer: Writes[JsValue]): Writes[A] = Writes[A] { a =>
transformer.writes(self.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
/**
* Transforms the resulting [[JsObject]] using a transformer function.
*/
def transform(transformer: JsObject => JsObject): OWrites[A] =
OWrites[A] { a =>
transformer(this.writes(a))
}
/**
* Transforms the resulting [[JsValue]] using a `Writes[JsValue]`.
*/
def transform(transformer: OWrites[JsObject]): OWrites[A] =
OWrites[A] { a =>
transformer.writes(this.writes(a))
}
override def contramap[B](f: B => A): OWrites[B] =
OWrites[B](b => this.writes(f(b)))
override def narrow[B <: A]: OWrites[B] = this.asInstanceOf[OWrites[B]]
}
object OWrites extends PathWrites with ConstraintWrites {
import play.api.libs.functional._
def of[A](implicit w: OWrites[A]): OWrites[A] = w
def pure[A](fixed: => A)(implicit wrs: OWrites[A]): OWrites[JsValue] =
OWrites[JsValue] { js =>
wrs.writes(fixed)
}
/**
* An `OWrites` merging the results of two separate `OWrites`.
*/
private object MergedOWrites {
def apply[A, B](wa: OWrites[A], wb: OWrites[B]): OWrites[A ~ B] =
new OWritesFromFields[A ~ B] {
def writeFields(fieldsMap: mutable.Map[String, JsValue], obj: A ~ B): Unit = {
val a ~ b = obj
mergeIn(fieldsMap, wa, a)
mergeIn(fieldsMap, wb, b)
}
}
@inline final def mergeIn[A](fieldsMap: mutable.Map[String, JsValue], wa: OWrites[A], a: A): Unit = wa match {
case wff: OWritesFromFields[A] =>
wff.writeFields(fieldsMap, a)
case w: OWrites[A] =>
w.writes(a).underlying.foreach {
case (key, value: JsObject) =>
fieldsMap.put(
key,
fieldsMap.get(key) match {
case Some(o: JsObject) => o.deepMerge(value)
case _ => value
}
)
case (key, value) =>
fieldsMap.put(key, value)
}
}
}
/**
* An `OWrites` capable of writing an object incrementally to a mutable map
*/
private trait OWritesFromFields[A] extends OWrites[A] {
def writeFields(fieldsMap: mutable.Map[String, JsValue], a: A): Unit
def writes(a: A): JsObject = {
import scala.collection.JavaConverters._
val fieldsMap = new java.util.LinkedHashMap[String, JsValue]()
writeFields(fieldsMap.asScala, a)
JsObject(new ImmutableLinkedHashMap(fieldsMap))
}
}
implicit val functionalCanBuildOWrites: FunctionalCanBuild[OWrites] = new FunctionalCanBuild[OWrites] {
def apply[A, B](wa: OWrites[A], wb: OWrites[B]): OWrites[A ~ B] = MergedOWrites[A, B](wa, wb)
}
implicit val contravariantfunctorOWrites: ContravariantFunctor[OWrites] = new ContravariantFunctor[OWrites] {
def contramap[A, B](wa: OWrites[A], f: B => A): OWrites[B] =
wa.contramap[B](f)
}
/**
* Returns an instance which uses `f` as [[OWrites.writes]] function.
*/
def apply[A](f: A => JsObject): OWrites[A] = new OWrites[A] {
def writes(a: A): JsObject = f(a)
}
/**
* Transforms the resulting [[JsObject]] using the given function,
* which is also applied with the initial input.
*
* @param w the initial writer
* @param f the transformer function
*/
def transform[A](w: OWrites[A])(f: (A, JsObject) => JsObject): OWrites[A] =
OWrites[A] { a =>
f(a, w.writes(a))
}
/**
* Writes a tuple of two values to a JSON object, with custom field names.
*
* @param name1 the name of the first field
* @param name2 the name of the second field
* @tparam A the type of the first value
* @tparam B the type of the second value
*
* {{{
* val tuple2Writes: OWrites[(String, Int)] =
* OWrites.tuple2[String, Int]("name", "age")
*
* tuple2Writes.writes("Bob" -> 30) // {"name":"Bob","age":30}
* }}}
*/
def tuple2[A: Writes, B: Writes](name1: String, name2: String): OWrites[(A, B)] = OWrites[(A, B)] { case (a, b) =>
Json.obj(name1 -> a, name2 -> b)
}
/**
* Writes a tuple of three values to a JSON object, with custom field names.
*
* @param name1 the name of the first field
* @param name2 the name of the second field
* @param name3 the name of the third field
* @tparam A the type of the first value
* @tparam B the type of the second value
* @tparam C the type of the third value
*
* {{{
* val tuple3Writes: OWrites[(String, Int, Boolean)] =
* OWrites.tuple3[String, Int, Boolean]("name", "age", "isStudent")
*
* tuple3Writes.writes(("Bob", 30, false))
* // {"name":"Bob","age":30,"isStudent":false}
* }}}
*/
def tuple3[A: Writes, B: Writes, C: Writes](name1: String, name2: String, name3: String): OWrites[(A, B, C)] =
OWrites[(A, B, C)] { case (a, b, c) =>
Json.obj(name1 -> a, name2 -> b, name3 -> c)
}
/**
* Writes a tuple of four values to a JSON object, with custom field names.
*
* @param name1 the name of the first field
* @param name2 the name of the second field
* @param name3 the name of the third field
* @param name4 the name of the fourth field
* @tparam A the type of the first value
* @tparam B the type of the second value
* @tparam C the type of the third value
* @tparam D the type of the fourth value
*
* {{{
* val tuple4Writes: OWrites[(String, Int, Boolean, Double)] =
* OWrites.tuple4[String, Int, Boolean, Double]("name", "age", "isStudent", "score")
*
* tuple4Writes.writes(("Bob", 30, false, 91.2))
* // {"name":"Bob","age":30,"isStudent":false,"score":91.2}
* }}}
*/
def tuple4[A: Writes, B: Writes, C: Writes, D: Writes](
name1: String,
name2: String,
name3: String,
name4: String
): OWrites[(A, B, C, D)] = OWrites[(A, B, C, D)] { case (a, b, c, d) =>
Json.obj(name1 -> a, name2 -> b, name3 -> c, name4 -> d)
}
}
/**
* Default Serializers.
*/
object Writes extends PathWrites with ConstraintWrites with DefaultWrites with GeneratedWrites {
val constraints: ConstraintWrites = this
val path: PathWrites = this
implicit val contravariantfunctorWrites: ContravariantFunctor[Writes] =
new ContravariantFunctor[Writes] {
def contramap[A, B](wa: Writes[A], f: B => A): Writes[B] =
wa.contramap[B](f)
}
/**
* Returns an instance which uses `f` as [[Writes.writes]] function.
*/
def apply[A](f: A => JsValue): Writes[A] = new Writes[A] {
def writes(a: A): JsValue = f(a)
}
/**
* Transforms the resulting [[JsValue]] using the given function,
* which is also applied with the initial input.
* def transform(transformer: (A, JsValue) => JsValue): Writes[A] =
* Writes[A] { a => transformer(a, this.writes(a)) }
*
* @param w the initial writer
* @param f the transformer function
*/
def transform[A](w: Writes[A])(f: (A, JsValue) => JsValue): Writes[A] =
Writes[A] { a =>
f(a, w.writes(a))
}
}
/**
* Default Serializers.
*/
trait DefaultWrites extends LowPriorityWrites with EnumerationWrites {
/**
* Serializer for Int types.
*/
implicit object IntWrites extends Writes[Int] {
def writes(o: Int) = JsNumber(o)
}
/**
* Serializer for Short types.
*/
implicit object ShortWrites extends Writes[Short] {
def writes(o: Short) = JsNumber(BigDecimal(o))
}
/**
* Serializer for Byte types.
*/
implicit object ByteWrites extends Writes[Byte] {
def writes(o: Byte) = JsNumber(BigDecimal(o))
}
/**
* Serializer for Long types.
*/
implicit object LongWrites extends Writes[Long] {
def writes(o: Long) = JsNumber(o)
}
/**
* Serializer for Float types.
*/
implicit object FloatWrites extends Writes[Float] {
def writes(o: Float) = JsNumber(BigDecimal.decimal(o))
}
/**
* Serializer for Double types.
*/
implicit object DoubleWrites extends Writes[Double] {
def writes(o: Double) = JsNumber(o)
}
/**
* Serializer for BigDecimal types.
*/
implicit object BigDecimalWrites extends Writes[BigDecimal] {
def writes(o: BigDecimal) = JsNumber(o)
}
/**
* Serializer for BigInt type.
*/
implicit object BigIntWrites extends Writes[BigInt] {
def writes(i: BigInt) = JsNumber(BigDecimal(i))
}
/**
* Serializer for BigInteger type.
*/
implicit object BigIntegerWrites extends Writes[java.math.BigInteger] {
def writes(i: java.math.BigInteger) = JsNumber(BigDecimal(i))
}
/**
* Serializer for Boolean types.
*/
implicit object BooleanWrites extends Writes[Boolean] {
def writes(o: Boolean) = JsBoolean(o)
}
/**
* Serializer for String types.
*/
implicit object StringWrites extends Writes[String] {
def writes(o: String) = JsString(o)
}
/**
* Serializer for Array[T] types.
*/
implicit def arrayWrites[T: ClassTag: Writes]: Writes[Array[T]] = {
val w = implicitly[Writes[T]]
Writes[Array[T]] { ts =>
JsArray(ts.map(w.writes(_)).toArray[JsValue])
}
}
/**
* Serializer for Map[String,V] types.
*/
@deprecated("Use `genericMapWrites`", "2.8.0")
def mapWrites[V: Writes]: OWrites[MapWrites.Map[String, V]] = MapWrites.mapWrites
implicit def keyMapWrites[K: KeyWrites, V: Writes, M[K, V] <: MapWrites.Map[K, V]]: OWrites[M[K, V]] = {
val kw = implicitly[KeyWrites[K]]
val vw = implicitly[Writes[V]]
OWrites[M[K, V]] { ts =>
JsObject(ts.toSeq.map { case (k, v) =>
kw.writeKey(k) -> vw.writes(v)
})
}
}
/**
* Serializer for Map[String,V] types.
*/
implicit def genericMapWrites[V, M[A, B] <: MapWrites.Map[A, B]](implicit w: Writes[V]): OWrites[M[String, V]] =
OWrites[M[String, V]] { ts =>
JsObject(ts.iterator.map { case (k, v) => k -> w.writes(v) }.toSeq)
}
@deprecated("Use `jsValueWrites`", "2.8.0")
object JsValueWrites extends Writes[JsValue] {
def writes(o: JsValue) = o
}
/**
* Serializer for JsValues.
*/
implicit def jsValueWrites[T <: JsValue]: Writes[T] = Writes[T] { js =>
js
}
/**
* Serializer for JsNull.
*
* {{{
* import play.api.libs.json.Json
*
* Json.obj("foo" -> None)
* // equivalent to Json.obj("foo" -> JsNull)
* }}}
*/
implicit val NoneWrites: Writes[None.type] =
Writes[None.type] { _ =>
JsNull
}
/**
* If `Some` is directly used (not as `Option`).
*
* {{{
* import play.api.libs.json.{ Json, Writes }
*
* def foo[T: Writes](writeableValue: T) =
* Json.obj("foo" -> Some(writeableValue))
* // equivalent to Json.obj("foo" -> writeableValue)
* }}}
*/
implicit def someWrites[T](implicit w: Writes[T]): Writes[Some[T]] =
Writes[Some[T]] { some =>
w.writes(some.get)
}
/**
* Serializer for Option.
*/
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
}
}
/**
* Serializer for java.util.Date
* @param pattern the pattern used by SimpleDateFormat
*/
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))
}
@deprecated("Use `defaultDateWrites`", "2.8.0")
object DefaultDateWrites extends Writes[Date] {
def writes(d: Date): JsValue = JsNumber(d.getTime)
}
/**
* Default Serializer java.util.Date -> JsNumber(d.getTime (nb of ms))
*/
implicit def defaultDateWrites[T <: Date]: Writes[T] =
Writes[T] { d =>
JsNumber(d.getTime)
}
/**
* Serializer for java.sql.Date
* @param pattern the pattern used by SimpleDateFormat
*/
@deprecated("Use `dateWrites`", "2.8.0")
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))
}
/**
* Serializer for java.util.UUID
*/
implicit object UuidWrites extends Writes[java.util.UUID] {
def writes(u: java.util.UUID) = JsString(u.toString)
}
/**
* Serializer for [[scala.collection.immutable.Range]]
* (aka specialized `Seq` of `Int`).
*/
implicit def rangeWrites[T <: Range]: Writes[T] = Writes[T] { range =>
// `iterableWrites` cannot be resolved for as,
// even if `Range <: Traversable[_]`, it's not a parametrized one
// and so doesn't accept a type parameter (doesn't match `M[_]` constraint).
JsArray(range.map(JsNumber(_)))
}
}
sealed trait LowPriorityWrites extends EnvWrites {
/**
* Serializer for java.net.URI
*/
implicit val uriWrites: Writes[java.net.URI] =
implicitly[Writes[String]].contramap[java.net.URI](_.toString)
@deprecated("Use `iterableWrites`", "2.8.0")
def traversableWrites[A: Writes]: Writes[Traversable[A]] = {
val w = implicitly[Writes[A]]
Writes[Traversable[A]] { as =>
val builder = mutable.ArrayBuilder.make[JsValue]
as.foreach { a =>
builder += w.writes(a)
}
JsArray(builder.result())
}
// Avoid resolution ambiguity with more specific Traversable Writes,
// such as OWrites.map
}
/**
* Serializer for Iterable types.
*
* Deprecated due to incompatibility with non `_[_]` shapes, #368.
*/
@deprecated("Use `iterableWrites2`", "2.8.1")
def iterableWrites[A, M[T] <: Iterable[T]](implicit w: Writes[A]): Writes[M[A]] =
iterableWrites2[A, M[A]]
/**
* Serializer for Iterable types.
*/
implicit def iterableWrites2[A, I](implicit ev: I <:< Iterable[A], w: Writes[A]): Writes[I] = {
// Use Iterable rather than Traversable, for 2.13 compat
Writes[I] { as =>
val builder = mutable.ArrayBuilder.make[JsValue]
as.foreach { (a: A) =>
builder += w.writes(a)
}
JsArray(builder.result())
}
// Avoid resolution ambiguity with more specific Iterable Writes,
// such as OWrites.map
}
/**
* Serializer for any type that is provided an implicit conversion to String
* (e.g. tagged types).
*/
implicit def stringableWrites[T](implicit conv: T => String): Writes[T] = Writes.StringWrites.contramap[T](conv)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy