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

com.rojoma.json.v3.codec.JsonEncode.scala Maven / Gradle / Ivy

The newest version!
package com.rojoma.json.v3
package codec

import scala.language.higherKinds
import scala.{collection => sc}
import sc.JavaConverters._
import scala.reflect.ClassTag
import java.{util => ju}
import java.{net => jn}

import ast._
import util.{WrapperJsonEncode, Strategy, JsonEnumStrategy}

trait JsonEncode[T] {
  def encode(x: T): JValue
}

/** Generally-useful json implicits. */
object JsonEncode extends com.rojoma.json.v3.`-impl`.codec.TupleEncode {
  def apply[T](implicit a: JsonEncode[T]): a.type = a
  def toJValue[T : JsonEncode](x: T): JValue = JsonEncode[T].encode(x)

  implicit def seqEncode[T, S[X] <: sc.Seq[X]](implicit tEncode: JsonEncode[T]) = new JsonEncode[S[T]] {
    def encode(x: S[T]): JValue = {
      if(x.nonEmpty)
        JArray(x.view.map(tEncode.encode))
      else
        JArray.empty
    }
  }

  implicit def arrayEncode[T: JsonEncode: ClassTag] = new JsonEncode[Array[T]] {
    def encode(x: Array[T]): JValue =
      if(x.length > 0)
        JArray(x.view.map(JsonEncode[T].encode))
      else
        JArray.empty
  }

  implicit def setEncode[T, S[X] <: sc.Set[X]](implicit tEncode: JsonEncode[T]) = new JsonEncode[S[T]] {
    def encode(x: S[T]): JValue = {
      if(x.nonEmpty)
        JArray(x.toSeq.view.map(tEncode.encode))
      else
        JArray.empty
    }
  }

  implicit def juListEncode[T: JsonEncode] = new JsonEncode[ju.List[T]] {
    def encode(x: ju.List[T]): JValue = {
      if(!x.isEmpty)
        JArray(x.asScala.view.map(JsonEncode[T].encode))
      else
        JArray.empty
    }
  }

  implicit def juSetEncode[T: JsonEncode] = new JsonEncode[ju.Set[T]] {
    def encode(x: ju.Set[T]): JValue = {
      if(!x.isEmpty)
        JArray(x.asScala.toSeq.view.map(JsonEncode[T].encode))
      else
        JArray.empty
    }
  }

  implicit object stringEncode extends JsonEncode[String] {
    def encode(x: String) = JString(x)
  }

  implicit object boolEncode extends JsonEncode[Boolean] {
    private val jtrue = JBoolean.canonicalTrue
    private val jfalse = JBoolean.canonicalFalse
    def encode(x: Boolean) = if(x) jtrue else jfalse
  }

  implicit object jbooleanEncode extends JsonEncode[java.lang.Boolean] {
    private val jtrue = JBoolean.canonicalTrue
    private val jfalse = JBoolean.canonicalFalse
    def encode(x: java.lang.Boolean) = if(x) jtrue else jfalse
  }

  implicit object byteEncode extends JsonEncode[Byte] {
    def encode(x: Byte) = JNumber(x)
  }

  implicit object jbyteEncode extends JsonEncode[java.lang.Byte] {
    def encode(x: java.lang.Byte) = JNumber(x)
  }

  implicit object shortEncode extends JsonEncode[Short] {
    def encode(x: Short) = JNumber(x)
  }

  implicit object jshortEncode extends JsonEncode[java.lang.Short] {
    def encode(x: java.lang.Short) = JNumber(x)
  }

  implicit object intEncode extends JsonEncode[Int] {
    def encode(x: Int) = JNumber(x)
  }

  implicit object jintegerEncode extends JsonEncode[java.lang.Integer] {
    def encode(x: java.lang.Integer) = JNumber(x)
  }

  implicit object longEncode extends JsonEncode[Long] {
    def encode(x: Long) = JNumber(x)
  }

  implicit object jlongEncode extends JsonEncode[java.lang.Long] {
    def encode(x: java.lang.Long) = JNumber(x)
  }

  implicit object bigintEncode extends JsonEncode[BigInt] {
    def encode(x: BigInt) = JNumber(x)
  }

  implicit object bigintegerEncode extends JsonEncode[java.math.BigInteger] {
    def encode(x: java.math.BigInteger) = JNumber(new BigInt(x))
  }

  implicit object floatEncode extends JsonEncode[Float] {
    def encode(x: Float) = JNumber(x)
  }

  implicit object jfloatEncode extends JsonEncode[java.lang.Float] {
    def encode(x: java.lang.Float) = JNumber(x)
  }

  implicit object doubleEncode extends JsonEncode[Double] {
    def encode(x: Double) = JNumber(x)
  }

  implicit object jdoubleEncode extends JsonEncode[java.lang.Double] {
    def encode(x: java.lang.Double) = JNumber(x)
  }

  implicit object bigdecimalEncode extends JsonEncode[BigDecimal] {
    def encode(x: BigDecimal) = JNumber(x)
  }

  implicit object jbigdecimalEncode extends JsonEncode[java.math.BigDecimal] {
    def encode(x: java.math.BigDecimal) = JNumber(BigDecimal(x))
  }

  implicit def jvalueEncode[T <: JValue] = new JsonEncode[T] {
    def encode(x: T) = x
  }

  implicit def fieldMapEncode[T, U, M[A, B] <: sc.Map[A, B]](implicit tEncode: FieldEncode[T], uEncode: JsonEncode[U]): JsonEncode[M[T,U]] = {
    if(tEncode eq FieldEncode.stringEncode) {
      // common enough to have its own impl, since it can be done more cheaply
      // We're taking advantage of the fact that modifying the thing that was
      // encoded has undefined effects on the encodee up to ".forced".
      new JsonEncode[M[String, U]] {
        def encode(x: M[String, U]) =
          if(x.nonEmpty) JObject(x.mapValues(uEncode.encode))
          else JObject.empty
      }.asInstanceOf[JsonEncode[M[T, U]]]
    } else {
      new JsonEncode[M[T, U]] {
        def encode(x: M[T, U]) =
          if(x.nonEmpty) {
            // In keeping with the general philosophy of compound encoders,
            // we'll encode as lazily as possible.
            val keysConverted: sc.Map[String, U] = x.map { case (k, v) => tEncode.encode(k) -> v }
            JObject(keysConverted.mapValues(uEncode.encode))
          } else JObject.empty
      }
    }
  }

  implicit def fieldJuMapEncode[T, U](implicit tEncode: FieldEncode[T], uEncode: JsonEncode[U]): JsonEncode[ju.Map[T,U]] = new JsonEncode[ju.Map[T, U]] {
    val scalaCodec = fieldMapEncode[T, U, sc.Map]
    def encode(x: ju.Map[T, U]) = scalaCodec.encode(x.asScala)
  }

  @deprecated(message = "Use fieldMapEncode instead", since="3.2.0")
  def mapEncode[T, M[U, V] <: sc.Map[U, V]](implicit tEncode: JsonEncode[T]) = fieldMapEncode[String, T, M]

  @deprecated(message = "Use fieldJuMapEncode instead", since="3.2.0")
  def juMapEncode[T: JsonEncode] = fieldJuMapEncode[String, T]

  // either is right-biased; if decoding as Right fails it tries Left;
  // if Left fails the whole thing fails.
  implicit def eitherEncode[L: JsonEncode, R: JsonEncode] = new JsonEncode[Either[L, R]] {
    def encode(x: Either[L,R]) = x match {
      case Left(left) => JsonEncode[L].encode(left)
      case Right(right) => JsonEncode[R].encode(right)
    }
  }

  @deprecated(message = "Use jlEnumEncode2 instead", since = "3.14.0")
  def jlEnumEncode[T <: java.lang.Enum[T]] = new JsonEncode[T] {
    @volatile private var trueEncode: JsonEncode[T] = null

    def encode(x: T) = {
      var e = trueEncode
      if(e == null) {
        e = new EnumCodec[T](x.getClass)
        trueEncode = e
      }
      e.encode(x)
    }
  }

  private class EnumCodec[T <: java.lang.Enum[T]](cls: Class[_]) extends JsonEncode[T] {
    private val converter: T => String = locally {
      val ann = cls.getAnnotation(classOf[JsonEnumStrategy])
      if(ann == null) {
        _.name
      } else ann.value match {
        case Strategy.Identity =>
          _.name
        case Strategy.Underscore =>
          val converted =
            cls.getMethod("values").invoke(null).asInstanceOf[Array[T]].map { item =>
              `-impl`.util.CamelSplit(item.name).map(_.toLowerCase).mkString("_").intern()
            }
          t => converted(t.ordinal)
      }
    }

    def encode(x: T) = {
      JString(converter(x))
    }
  }

  implicit def jlEnumEncode2[T <: java.lang.Enum[T]](implicit ev: ClassTag[T]): JsonEncode[T] =
    new EnumCodec[T](ev.runtimeClass)

  implicit object UnitEncode extends JsonEncode[Unit] {
    private val empty = JArray.empty
    def encode(x: Unit) = empty
  }

  implicit val uuidEncode = WrapperJsonEncode[ju.UUID](_.toString)
  implicit val uriEncode = WrapperJsonEncode[jn.URI](_.toString)

  def scalaEnumEncode[T <: Enumeration](enum: T): JsonEncode[enum.Value] =
    JsonCodec.scalaEnumCodec(enum)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy