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

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

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

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

import ast._
import util.{WrapperJsonDecode, JsonCaseInsensitiveEnum, JsonEnumStrategy, Strategy}

trait JsonDecode[T] {
  def decode(x: JValue): JsonDecode.DecodeResult[T]
}

/** Generally-useful json implicits. */
object JsonDecode  extends com.rojoma.json.v3.`-impl`.codec.TupleDecode {
  private type CB[A, B] = sc.generic.CanBuild[A, B]
  type DecodeResult[T] = Either[DecodeError, T]

  def apply[T](implicit a: JsonDecode[T]): a.type = a
  def fromJValue[T : JsonDecode](x: JValue) = JsonDecode[T].decode(x)

  private class IterableDecode[T, S](tDecode: JsonDecode[T], buildFactory: CB[T, S]) extends JsonDecode[S] {
    def decode(xs: JValue): DecodeResult[S] = xs match {
      case JArray(jElems) =>
        val builder = buildFactory()
        for ((jElem, idx) <- jElems.iterator.zipWithIndex) {
          tDecode.decode(jElem) match {
            case Right(elem) =>
              builder += elem
            case Left(err) =>
              return Left(err.prefix(idx))
          }
        }
        Right(builder.result())
      case other =>
        Left(DecodeError.InvalidType(JArray, other.jsonType))
    }
  }

  implicit def seqDecode[T, S[X] <: sc.Seq[X]](implicit tDecode: JsonDecode[T], buildFactory: CB[T, S[T]]): JsonDecode[S[T]] =
    new IterableDecode(tDecode, buildFactory)

  implicit def arrayDecode[T](implicit tDecode: JsonDecode[T], ct: ClassTag[T]): JsonDecode[Array[T]] =
    new IterableDecode(tDecode, implicitly[CB[T, Array[T]]])

  implicit def setDecode[T, S[U] <: sc.Set[U]](implicit tCodec: JsonDecode[T], buildFactory: CB[T, S[T]]): JsonDecode[S[T]] =
    new IterableDecode(tCodec, buildFactory)

  implicit def juListDecode[T: JsonDecode] = new JsonDecode[ju.List[T]] {
    def decode(xs: JValue): DecodeResult[ju.List[T]] = xs match {
      case JArray(jElems) =>
        val result = new ju.ArrayList[T](jElems.length)
        val subDecode = JsonDecode[T]
        for((jElem, idx) <- jElems.iterator.zipWithIndex) {
          subDecode.decode(jElem) match {
            case Right(elem) =>
              result.add(elem)
            case Left(err) =>
              return Left(err.prefix(idx))
          }
        }
        Right(result)
      case other =>
        Left(DecodeError.InvalidType(JArray, other.jsonType))
    }
  }

  implicit def juSetDecode[T : JsonDecode] = new JsonDecode[ju.Set[T]] {
    def decode(xs: JValue): DecodeResult[ju.Set[T]] = xs match {
      case JArray(jElems) =>
        val result = new ju.LinkedHashSet[T]
        val subDecode = JsonDecode[T]
        for((jElem, idx) <- jElems.iterator.zipWithIndex) {
          subDecode.decode(jElem) match {
            case Right(elem) =>
              result.add(elem)
            case Left(err) =>
              return Left(err.prefix(idx))
          }
        }
        Right(result)
      case other =>
        Left(DecodeError.InvalidType(JArray, other.jsonType))
    }
  }

  implicit object stringDecode extends JsonDecode[String] {
    def decode(x: JValue) = x match {
      case JString(s) => Right(s)
      case other => Left(DecodeError.InvalidType(JString, other.jsonType))
    }
  }

  implicit object boolDecode extends JsonDecode[Boolean] {
    def decode(x: JValue) = x match {
      case JBoolean(b) => Right(b)
      case other => Left(DecodeError.InvalidType(JBoolean, other.jsonType))
    }
  }

  implicit object jbooleanDecode extends JsonDecode[java.lang.Boolean] {
    def decode(x: JValue) = x match {
      case JBoolean(b) => Right(b)
      case other => Left(DecodeError.InvalidType(JBoolean, other.jsonType))
    }
  }

  implicit object byteDecode extends JsonDecode[Byte] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toByte)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jbyteDecode extends JsonDecode[java.lang.Byte] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toByte)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object shortDecode extends JsonDecode[Short] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toShort)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jshortDecode extends JsonDecode[java.lang.Short] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toShort)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object intDecode extends JsonDecode[Int] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toInt)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jintegerDecode extends JsonDecode[java.lang.Integer] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toInt)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object longDecode extends JsonDecode[Long] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toLong)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jlongDecode extends JsonDecode[java.lang.Long] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toLong)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object bigintDecode extends JsonDecode[BigInt] {
    def encode(x: BigInt) = JNumber(x)
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toBigInt)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object bigintegerDecode extends JsonDecode[java.math.BigInteger] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toBigInt.underlying)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object floatDecode extends JsonDecode[Float] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toFloat)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jfloatDecode extends JsonDecode[java.lang.Float] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toFloat)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object doubleDecode extends JsonDecode[Double] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toDouble)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jdoubleDecode extends JsonDecode[java.lang.Double] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toDouble)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object bigdecimalDecode extends JsonDecode[BigDecimal] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toBigDecimal)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit object jbigdecimalDecode extends JsonDecode[java.math.BigDecimal] {
    def decode(x: JValue) = x match {
      case num: JNumber => Right(num.toBigDecimal.underlying)
      case other => Left(DecodeError.InvalidType(JNumber, other.jsonType))
    }
  }

  implicit def jvalueDecode[T <: JValue : Json] = new JsonDecode[T] {
    def decode(x: JValue) = x.cast[T] match {
      case Some(j) =>
        Right(j)
      case None =>
        val choices = implicitly[Json[T]].jsonTypes
        Left(DecodeError.join(choices.map(DecodeError.InvalidType(_, x.jsonType))))
    }
  }

  implicit def fieldMapDecode[T, U, M[A, B] <: sc.Map[A, B]](implicit tDecode: FieldDecode[T], uDecode: JsonDecode[U], buildFactory: CB[(T, U), M[T, U]]) = new JsonDecode[M[T, U]] {
    def decode(x: JValue): DecodeResult[M[T, U]] = x match {
      case JObject(fields) =>
        val builder = buildFactory()
        for((kv, jv) <- fields) {
          tDecode.decode(kv) match {
            case Right(k) =>
              uDecode.decode(jv) match {
                case Right(v) => builder += (k -> v)
                case Left(err) => return Left(err.prefix(kv))
              }
            case Left(err) =>
              return Left(err)
          }
        }
        Right(builder.result())
      case other =>
        Left(DecodeError.InvalidType(JObject, other.jsonType))
    }
  }

  implicit def fieldJuMapDecode[T, U](implicit tDecode: FieldDecode[T], uDecode: JsonDecode[U]) = new JsonDecode[ju.Map[T, U]] {
    def decode(x: JValue): DecodeResult[ju.Map[T, U]] = x match {
      case JObject(fields) =>
        val result = new ju.LinkedHashMap[T, U]
        for((kv, jv) <- fields) {
          tDecode.decode(kv) match {
            case Right(k) =>
              uDecode.decode(jv) match {
                case Right(v) => result.put(k, v)
                case Left(err) => return Left(err.prefix(kv))
              }
            case Left(err) =>
              return Left(err)
          }
        }
        Right(result)
      case other =>
        Left(DecodeError.InvalidType(JObject, other.jsonType))
    }
  }

  @deprecated(message = "Use fieldMapEncode instead", since="3.2.0")
  def mapDecode[T, M[U, V] <: sc.Map[U, V]](implicit tDecode: JsonDecode[T], buildFactory: CB[(String, T), M[String, T]]) =
    fieldMapDecode[String, T, M]

  @deprecated(message = "Use fieldJuMapEncode instead", since="3.2.0")
  def juMapDecode[T: JsonDecode] = fieldJuMapDecode[String, T]

  // either is right-biased; if decoding as Right fails it tries Left;
  // if Left fails the whole thing fails.
  implicit def eitherDecode[L: JsonDecode, R: JsonDecode] = new JsonDecode[Either[L, R]] {
    def decode(x: JValue) =
      JsonDecode[R].decode(x) match {
        case Right(right) => Right(Right(right))
        case Left(err1) =>
          JsonDecode[L].decode(x) match {
            case Right(left) => Right(Left(left))
            case Left(err2) =>
              Left(DecodeError.join(Seq(err1, err2)))
          }
      }
  }

  implicit def jlEnumDecode[T <: java.lang.Enum[T]](implicit tag: ClassTag[T]) = {
    val isUnderscoreized = Option(tag.runtimeClass.getAnnotation(classOf[JsonEnumStrategy])).
      fold(false) { ann =>
        ann.value() match {
          case Strategy.Underscore =>
            true
          case Strategy.Identity =>
            false
        }
      }
    val isCaseInsensitive = tag.runtimeClass.isAnnotationPresent(classOf[JsonCaseInsensitiveEnum])
    if(isUnderscoreized || isCaseInsensitive) {
      val nameMap =
        tag.
          runtimeClass.
          asInstanceOf[Class[T]].
          getMethod("values").
          invoke(null).
          asInstanceOf[Array[T]].
          iterator.
          map { e =>
            val name =
              if(isUnderscoreized) `-impl`.util.CamelSplit(e.name).mkString("_").toLowerCase.intern()
              else if(isCaseInsensitive) e.name.toLowerCase.intern()
              else e.name
            name -> e
          }.
          toMap

      if(isCaseInsensitive) {
        new JsonDecode[T] {
          def decode(x: JValue) = x match {
            case str@JString(s) =>
              nameMap.get(s.toLowerCase) match {
                case Some(e) ⇒ Right(e)
                case None ⇒ Left(DecodeError.InvalidValue(str))
              }
            case other =>
              Left(DecodeError.InvalidType(JString, other.jsonType))
          }
        }
      } else {
        new JsonDecode[T] {
          def decode(x: JValue) = x match {
            case str@JString(s) =>
              nameMap.get(s) match {
                case Some(e) ⇒ Right(e)
                case None ⇒ Left(DecodeError.InvalidValue(str))
              }
            case other =>
              Left(DecodeError.InvalidType(JString, other.jsonType))
          }
        }
      }
    } else {
      new JsonDecode[T] {
        def decode(x: JValue) = x match {
          case str@JString(s) =>
            try {
              Right(java.lang.Enum.valueOf[T](tag.runtimeClass.asInstanceOf[Class[T]], s))
            } catch {
              case _: IllegalArgumentException =>
                Left(DecodeError.InvalidValue(str))
            }
          case other =>
            Left(DecodeError.InvalidType(JString, other.jsonType))
        }
      }
    }
  }

  implicit object UnitDecode extends JsonDecode[Unit] {
    def decode(x: JValue) = x match {
      case JArray(xs) if xs.isEmpty => Right(())
      case nonEmpty: JArray => Left(DecodeError.InvalidLength(0, nonEmpty.length))
      case other => Left(DecodeError.InvalidType(JArray, other.jsonType))
    }
  }

  implicit val uuidDecode = WrapperJsonDecode[ju.UUID](ju.UUID.fromString)
  implicit val uriDecode = WrapperJsonDecode[jn.URI](jn.URI.create)

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy