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

lihua.mongo.JsonFormats.scala Maven / Gradle / Ivy

/*
* Copyright [2017] [iHeartMedia Inc]
* All rights reserved
*/
package lihua
package mongo

import cats.Invariant
import org.joda.time.DateTime
import play.api.libs.json.Json.toJson
import play.api.libs.json._

import scala.reflect.ClassTag

object JsonFormats {

  implicit object EntityIdFormat extends Format[EntityId] {

    override def reads(json: JsValue): JsResult[EntityId] = (json \ "$oid").validate[String].map(EntityId(_))

    override def writes(o: EntityId): JsValue = Json.obj("$oid" → o.value)
  }


  implicit def entityFormat[T: Format]: OFormat[Entity[T]] = new OFormat[Entity[T]] {
    def writes(e: Entity[T]): JsObject =
      toJson(e.data).as[JsObject] + ("_id" -> toJson(e._id))

    def reads(json: JsValue): JsResult[Entity[T]] = for {
      id <- (json \ "_id").validate[EntityId]
      t <- json.validate[T]
    } yield Entity(id, t)
  }


  implicit val invariantFormat: Invariant[Format] = new Invariant[Format] {
    def imap[A, B](fa: Format[A])(f: A => B)(g: B => A): Format[B] = new Format[B] {
      override def reads(json: JsValue): JsResult[B] = fa.reads(json).map(f)
      override def writes(o: B): JsValue = fa.writes(g(o))
    }
  }


  implicit object JodaFormat extends Format[DateTime] {

    override def reads(json: JsValue): JsResult[DateTime] = (json \ "$date").validate[Long].map(new DateTime(_))

    override def writes(o: DateTime): JsValue = Json.obj("$date" → o.getMillis)
  }

  object StringBooleanFormat extends Format[Boolean] {

    override def reads(json: JsValue): JsResult[Boolean] = json.validate[String].map(_.toLowerCase == "true")

    override def writes(o: Boolean): JsValue = JsString(o.toString)
  }

  object IntBooleanFormat extends Format[Boolean] {

    override def reads(json: JsValue): JsResult[Boolean] = json.validate[Int].map(_ != 0)

    override def writes(o: Boolean): JsValue = JsNumber(if (o) 1 else 0)
  }

  implicit class JsPathMongoDBOps(val self: JsPath) extends AnyVal {
    def formatEntityId = OFormat[String](self.read[EntityId].map(_.value), OWrites[String] { s ⇒ self.write[EntityId].writes(EntityId(s)) })
  }

  implicit def mapFormat[KT: StringParser, VT: Format]: Format[Map[KT, VT]] = new Format[Map[KT, VT]] {
    def writes(o: Map[KT, VT]): JsValue =
      JsObject(o.toSeq.map { case (k, v) ⇒ (k.toString, Json.toJson(v)) })

    def reads(json: JsValue): JsResult[Map[KT, VT]] =
      json.validate[JsObject].map(_.fields.map {
        case (ks, vValue) ⇒ (implicitly[StringParser[KT]].parse(ks), vValue.as[VT])
      }.toMap)
  }

  private def myClassOf[T: ClassTag] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]

  implicit def javaEnumWrites[ET <: Enum[ET]]: Writes[ET] = Writes {
    case r: Enum[_] ⇒ JsString(r.name())
  }

  implicit def javaEnumReads[ET <: Enum[ET]: ClassTag]: Reads[ET] = Reads {
    case JsString(name) ⇒
      JsSuccess(Enum.valueOf(myClassOf[ET], name))
    //TODO: improve error
    case _ ⇒ JsError("unrecognized format")
  }

  implicit def javaEnumFormats[ET <: Enum[ET]: ClassTag]: Format[ET] = Format(javaEnumReads[ET], javaEnumWrites[ET])

  trait StringParser[T] {
    def parse(s: String): T
  }

  object StringParser {
    implicit val intStringParser: StringParser[Int] = new StringParser[Int] {
      def parse(s: String): Int = java.lang.Integer.parseInt(s)
    }

    implicit val stringStringParser: StringParser[String] = new StringParser[String] {
      def parse(s: String): String = s
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy