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

jsondbc.Json4sSPI.scala Maven / Gradle / Ivy

The newest version!
package jsondbc

import monocle.function.Each
import monocle.{Iso, PTraversal, Prism, Traversal}
import org.json4s.JsonAST._
import scalaz.Applicative

import scala.language.higherKinds

object Json4sSPI extends Json4sSPI
trait Json4sSPI {
  implicit val json4sSPI: SPI.Aux[JValue, JObject, JDecimal] = new SPI[JValue] {
    type JsonObject = JObject
    type JsonNumber = JDecimal

    def jField(json: JValue, name: String): Option[JValue] = {
      for {
        obj <- jObject.getOption(json)
        value <- obj.obj.collectFirst {
          case JField(`name`, value) => value
        }
      } yield value
    }

    val ordering: Ordering[JValue] = {
      implicit val recurse: Ordering[JValue] = (x: JValue, y: JValue) => ordering.compare(x, y)

      Ordering.Tuple5[Option[Boolean], Option[Int], Option[Double], Option[String], Option[Iterable[(String, JValue)]]].on[JValue](json ⇒ {
        (asBoolean(json), asInt(json), asDouble(json), asString(json), asJsObject(json).map(_.obj))
      })
    }

    val jNull: Prism[JValue, Unit] = prism[Unit](jsonNull = Some(()))(_ => JNull)
    val jObject: Prism[JValue, JObject] = Prism[JValue, JObject](asJsObject)(identity)
    val jArray: Prism[JValue, List[JValue]] = prism[List[JValue]](jsonArray = arr => Some(arr))(JArray(_))
    val jBoolean: Prism[JValue, Boolean] = Prism[JValue, Boolean](asBoolean)(JBool(_))
    val jNumber: Prism[JValue, JDecimal] = prism[JDecimal](jsonNumber = Some(_))(identity)
    val jDouble: Prism[JValue, Double] = Prism[JValue, Double](asDouble)(JDouble(_))
    val jString: Prism[JValue, String] = Prism[JValue, String](asString)(JString)
    val jBigDecimal: Prism[JValue, BigDecimal] = prism[BigDecimal](jsonNumber = jd => Some(jd.num))(value => JDecimal(value))
    val jBigInt: Prism[JValue, BigInt] = prism[BigInt](jsonNumber = jd => Some(jd.num.toBigInt()))(value => JDecimal(BigDecimal(value)))
    val jLong: Prism[JValue, Long] = prism[Long](jsonLong = Some(_))(JLong(_))
    val jInt: Prism[JValue, Int] = Prism[JValue, Int](asInt)(value => JInt(BigInt(value)))
    val jShort: Prism[JValue, Short] = prism[Short](jsonNumber = jd => Some(jd.num.toShort))(value => JDecimal(BigDecimal(value)))
    val jByte: Prism[JValue, Byte] = prism[Byte](jsonNumber = jd => Some(jd.num.toByte))(value => JDecimal(BigDecimal(value)))

    def jObjectMap: Iso[JObject, Map[String, JValue]] =
      Iso[JObject, Map[String, JValue]](_.obj.toMap)(m => JObject(m.toList))

    val jDescendants: Traversal[JValue, JValue] = new PTraversal[JValue, JValue, JValue, JValue] {
      override def modifyF[F[_]](f: JValue => F[JValue])(s: JValue)(implicit F: scalaz.Applicative[F]): F[JValue] =
        jsFold(F.pure(s), _ => F.pure(s), _ => F.pure(s), _ => F.pure(s), _ => F.pure(s), _ => F.pure(s), _ => F.pure(s),
          arr => F.map(Each.each[List[JValue], JValue].modifyF(f)(arr))(JArray(_): JValue),
          obj => F.map(jObjectValues.modifyF(f)(obj))(jsObject => jsObject: JValue)
        )(s)
    }

    val jObjectValues: Traversal[JObject, JValue] = new PTraversal[JObject, JObject, JValue, JValue] {
      def modifyF[F[_]](f: JValue => F[JValue])(o: JObject)(implicit F: Applicative[F]): F[JObject] = {
        F.map(o.obj.foldLeft(F.point(List[(String, JValue)]())) {
          case (acc, (k, v)) => F.apply2(acc, f(v)) {
            case (elems, newV) => (k, newV) :: elems
          }
        })(elems => JObject(elems.reverse: _*))
      }
    }

    def filterObject(p: String => Boolean): Traversal[JObject, JValue] = {
      new PTraversal[JObject, JObject, JValue, JValue] {
        import scalaz.std.list._
        import scalaz.syntax.applicative._
        import scalaz.syntax.traverse._

        def modifyF[F[_]: Applicative](f: JValue => F[JValue])(from: JObject): F[JObject] =
          Applicative[F].map(
            from.obj.traverse[F, (String, JValue)]{ case (field, json) =>
              Applicative[F].map(if (p(field)) f(json) else json.point[F])(field -> _)
            }
          )(elems => {
            JObject(elems.reverse: _*)
          })
      }
    }


    private def asBoolean:  JValue => Option[Boolean]  = opt(jsonBool   = Some(_))
    private def asString:   JValue => Option[String]   = opt(jsonString = Some(_))

    private def asInt:      JValue => Option[Int]      = opt(
      jsonInt  = value => Some(value.num.toInt),
      jsonLong = value => Some(value.toInt)
    )

    private def asDouble:   JValue => Option[Double]   = opt(jsonDouble = Some(_))
    private def asJsObject: JValue => Option[JObject]  = opt(jsonObject = Some(_))

    private def opt[A](
      jsonNull:                => Option[A] = None,
      jsonBool:   Boolean      => Option[A] = (_: Boolean) => None,
      jsonNumber: JDecimal     => Option[A] = (_: JDecimal) => None,
      jsonDouble: Double       => Option[A] = (_: Double) => None,
      jsonInt:    JInt         => Option[A] = (_: JInt) => None,
      jsonLong:   Long         => Option[A] = (_: Long) => None,
      jsonString: String       => Option[A] = (_: String) => None,
      jsonArray:  List[JValue] => Option[A] = (_: List[JValue]) => None,
      jsonObject: JObject      => Option[A] = (_: JObject) => None
    ): JValue => Option[A] = jsFold(jsonNull, jsonBool, jsonNumber, jsonDouble, jsonInt, jsonLong, jsonString, jsonArray, jsonObject)

    private def prism[A](
      jsonNull:                => Option[A] = None,
      jsonBool:   Boolean      => Option[A] = (_: Boolean) => None,
      jsonNumber: JDecimal     => Option[A] = (_: JDecimal) => None,
      jsonDouble: Double       => Option[A] = (_: Double) => None,
      jsonInt:    JInt         => Option[A] = (_: JInt) => None,
      jsonLong:   Long         => Option[A] = (_: Long) => None,
      jsonString: String       => Option[A] = (_: String) => None,
      jsonArray:  List[JValue] => Option[A] = (_: List[JValue]) => None,
      jsonObject: JObject      => Option[A] = (_: JObject) => None
    )(jsonValue: A => JValue): Prism[JValue, A] = Prism[JValue, A](
      jsFold(jsonNull, jsonBool, jsonNumber, jsonDouble, jsonInt, jsonLong, jsonString, jsonArray, jsonObject)
    )(jsonValue)

    private def jsFold[A](
      jsonNull:                => A,
      jsonBool:   Boolean      => A,
      jsonNumber: JDecimal     => A,
      jsonDouble: Double       => A,
      jsonInt:    JInt         => A,
      jsonLong:   Long         => A,
      jsonString: String       => A,
      jsonArray:  List[JValue] => A,
      jsonObject: JObject      => A
    )(jsValue: JValue): A = jsValue match {
      case JNothing | JNull => jsonNull
      case JBool(value)     => jsonBool(value)
      case value: JDecimal  => jsonNumber(value)
      case JDouble(value)   => jsonDouble(value)
      case JLong(value)     => jsonLong(value)
      case value: JInt      => jsonInt(value)
      case JString(value)   => jsonString(value)
      case JArray(arr)      => jsonArray(arr)
      case JSet(set)        => jsonArray(set.toList)
      case value: JObject   => jsonObject(value)
    }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy