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

play.api.libs.json.ops.v4.Exceptions.scala Maven / Gradle / Ivy

The newest version!
package play.api.libs.json.ops.v4

import play.api.libs.json._

import scala.reflect._
import scala.reflect.runtime.universe._

private[ops] object Exceptions {

  /**
    * Returns the estimated literal string of code you would use to match on this value.
    */
  def keyAsCodeLiteral(keyValue: Any): String = {
    keyValue match {
      case s: String => "\"%s\"".format(s) // for some reason string interpolation goes wonky here in Scala 2.11
      case _ => keyValue.toString // this should work for most case classes and primitive types
    }
  }

  /**
    * Returns the path to the key in a more code-friendly format.
    */
  def keyPathAsString(keyPath: JsPath): String = {
    val str = keyPath.toJsonString
    if (str == "obj") "__" else str
  }
}

/**
  * An exception representing the failure to match a type key to the specific serializer
  * for that type.
  *
  * @param valueType the type object of the generic type that could not be serialized
  * @param keyValue the value of the key that was unmatched with a serializer
  * @param keyPath the path to the key
  * @param keyType the type object of the key
  */
class UnrecognizedTypeKeyException(
  val valueType: Type,
  val keyValue: Any,
  val keyPath: JsPath,
  val keyType: Type
  ) extends RuntimeException({
  val keyValueStr = Exceptions.keyAsCodeLiteral(keyValue)
  val keyPathString = Exceptions.keyPathAsString(keyPath)
  val keyPathWithType = s"$keyPathString.as[$keyType]"
  s"Cannot parse instance of $valueType.\n" +
    s"Unrecognized key '$keyValue' as extracted at path with type $keyPathWithType\n" +
    "Please define the following in the Json.formatAbstract for this type:\n" +
    s"  case $keyValueStr => OFormat.of[_ <: $valueType]\n"
})

/**
  * Thrown when the type key cannot be deserialized from Json.
  *
  * @param valueType the type object of the generic type that could not be serialized
  * @param jsonKey the key as written in Json
  * @param readErrors the [[JsError]] values accumulated when parsing the type key
  */
class JsonTypeKeyReadException(
  val valueType: Type,
  val jsonKey: JsObject,
  val readErrors: JsError
) extends RuntimeException({
  val jsonKeyString = Json.prettyPrint(jsonKey)
  val readErrorsString = Json.prettyPrint(JsError.toJson(readErrors))
  s"Cannot write an instance of $valueType.\n" +
    s"The model's key was writen as Json: $jsonKeyString\n" +
    s"but could not be converted into a type key because of the following validation errors: $readErrorsString\n" +
    "Please make sure this object can be parsed by exactly one of the readers in the defined by:\n" +
    s"  Json.extractTypeKeyOf[$valueType].usingKeyObject()\n"
})

/**
  * Thrown when the wrong [[OFormat]] is chosen in the partial function given to [[AbstractJsonOps.formatAbstract]].
  *
  * @param valueType the type object of the generic type that could not be serialized
  * @param keyValue the value of the key that was unmatched with a serializer
  * @param keyPath the path to the key
  * @param keyType the type object of the key
  * @param cause the [[ClassCastException]] that caused this exception
  */
class WrongAbstractFormatException(
  val valueType: Type,
  val keyValue: Any,
  val keyPath: JsPath,
  val keyType: Type,
  cause: ClassCastException
) extends RuntimeException({
  val keyPathString = Exceptions.keyPathAsString(keyPath)
  val keyPathWithType = s"$keyPathString.as[$keyType]"
  s"Cannot parse instance of $valueType.\n" +
    s"The OFormat chosen by the given key '$keyValue' as extracted at path with type $keyPathWithType " +
    "encountered a ClassCastException\n" +
    "Please be sure that the definition of Json.formatAbstract uses the correct OFormat for this type.\n"
}, cause)

/**
  * An exception that provides better error messaging than the standard [[JsResultException]].
  *
  * @note the constructor is private to prevent accidentally creating the exception without
  *       applying the appropriate implicit transform.
  * @param json the json that was being parsed (note, this is printed verbatim, be sure to redact
  *             any sensitive information.
  * @param error the [[JsError]] encountered while parsing the json
  */
class InvalidJsonException private[InvalidJsonException] (val expectedClass: Class[_], val json: JsValue, val error: JsError)
  extends RuntimeException(
    s"Could not read an instance of ${expectedClass.getName} from:\n" +
      s"${Json.prettyPrint(json)}\n" +
      s"Errors encountered:\n" +
      s"${Json.prettyPrint(JsError.toJson(error))}"
  )

object InvalidJsonException {

  /**
    * Builds an instance of [[InvalidJsonException]] applying the appropriate [[JsonTransform]].
    *
    * @param rawJson the json that was being parsed (before applying the transform)
    * @param error the [[JsError]] encountered while parsing the json
    * @tparam A the type that was attempting to be parsed
    */
  def apply[A: JsonTransform: ClassTag](rawJson: JsValue, error: JsError): InvalidJsonException = {
    val json = JsonTransform.transform(rawJson)
    new InvalidJsonException(classTag[A].runtimeClass, json, error)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy