Permalink
Switch branches/tags
backport/0.1.2/66 backport/0.2.1/110 backport/0.4.1/273 backport/0.5.2/366 backport/0.5.3 backport/0.5.4 backport/0.7.1 backport/0.9.2 backport/0.9.3 backport/0.10.1 downloads/2017-09 downloads/2017-11 downloads/2017-12 downloads/2018-01 downloads/2018-02 downloads/2018-04 downloads/2018-05 downloads/2018-06 downloads/2018-07 downloads/2018-08 downloads/2018-09 downloads/2018-10 downloads/2018-11 felixbr/topic-917-better-java8-time-errors fix/65 fix/217 fix/320 fix/679 fix/760 fix/816 fix/831 fix/865 fix/1023 gh-pages master optimization/seq-decoder rework/json-number scalajs starter/320 topic/abstract-decoder topic/accumulating-decoders topic/adt-enumeration-derivation topic/aggregate-benchmarks-hygiene topic/allow-transforming-discriminator-values topic/ast-module topic/async topic/cats-0.4.0 topic/cats-0.8.0 topic/circe-iteratee-link-fix topic/circe-iteratee-link topic/circe-org-list topic/codec-only topic/codec-serializability topic/configuring-generic-derivation topic/contributing topic/cursor-optimization topic/de-gd-benchmark topic/de-triemap topic/de-truncate topic/de-whitebox-ification topic/decoder-either topic/derivation-overhaul topic/docs-ensure-validate topic/ensure-tests topic/export-hook-1.0.2 topic/export-hook-again topic/fix-docs topic/fix-ensure topic/fix-java8 topic/fix-key-decoder topic/fix-microsite-optics topic/fix-readme topic/fix-scala.js-2.13 topic/fix-sorted-map-encoding topic/fix-travis-ci topic/flatten-numbers topic/floats-again topic/follow-up-642 topic/for-product-n-reorder topic/for-sum-n topic/gd2-tests topic/generic-extras-laws topic/hook-less topic/java-math-instances topic/java-time-to-core topic/jawn-perf-java topic/jawn-perf topic/js-printing topic/json-number-big-decimal topic/json-number-parser topic/kleisli-instantiation topic/less-paradise topic/lockdown topic/mod-tests topic/more-memory topic/non-empty-x-instances topic/null-eq topic/optics-move topic/optics-removal topic/post-0.9.0-clean-up topic/printer-fix topic/printing-folder topic/printing-vector-iterator topic/reinstate-java8 topic/remove-0.9-deprecations topic/remove-errors topic/remove-finite-duration-codec topic/repr-codecs topic/repro-build topic/sbt-scalajs-crossproject topic/scaladoc-improvements topic/scaladoc-links topic/scalafmt topic/simplify-printing topic/sketchy-optimization topic/sonatype-data topic/split-generic-tests topic/spray-results topic/test-coverage topic/type-equality topic/unsafety topic/update-examples topic/update-previous topic/use-shrink topic/waiter-melon/regex-opt topic/xor-less-opt topic/2.13-cleanup topic/994-but-accumulating travis/chains travis/export-encoder travis/java-time-instance-clean-up travis/update-misc tut-site update/cats-1.0.0-RC1 update/cats-1.2.0 update/cats-1.3.0 update/cats-1.3.1 update/cats-1.4.0 update/cats-1.5.0-RC0 update/coursier-1.0.0-RC6 update/iteratee-0.14.0 update/jawn-0.12.1 update/jawn-0.13.0 update/monocle-1.5.0 update/paradise-2.1.1 update/prep-2.13.0-M4 update/refined-0.8.6 update/refined-0.9.0 update/sbt-1.0.3 update/sbt-1.1.0 update/sbt-1.1.1 update/sbt-1.1.6 update/sbt-1.2.3 update/sbt-1.2.7 update/sbt-plugins update/scala-2.11.12 update/scala-2.12.4 update/scala-2.12.5 update/scala-2.12.6 update/scala-2.12.8 update/scala.js-0.6.21 update/scala.js-0.6.22 update/scala.js-0.6.24 update/scala.js-0.6.25 update/scoverage-1.4.0 update/shapeless-2.3.3 update/2.13.0-M4
Nothing to show
Find file Copy path
1149 lines (1028 sloc) 38 KB
package io.circe
import cats.{ MonadError, SemigroupK }
import cats.data.{
Chain,
Kleisli,
NonEmptyChain,
NonEmptyList,
NonEmptyMap,
NonEmptySet,
NonEmptyVector,
StateT,
Validated
}
import cats.data.Validated.{ Invalid, Valid }
import cats.instances.either.{ catsStdInstancesForEither, catsStdSemigroupKForEither }
import cats.kernel.Order
import io.circe.export.Exported
import java.io.Serializable
import java.util.UUID
import scala.annotation.tailrec
import scala.collection.immutable.{ Map => ImmutableMap, Set, SortedMap, SortedSet }
import scala.collection.mutable.Builder
import scala.util.{ Failure, Success, Try }
trait Decoder[A] extends Serializable { self =>
/**
* Decode the given [[HCursor]].
*/
def apply(c: HCursor): Decoder.Result[A]
private[circe] def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] = apply(c) match {
case Right(a) => Validated.valid(a)
case Left(e) => Validated.invalidNel(e)
}
/**
* Decode the given [[ACursor]].
*
* Note that if you override the default implementation, you should also be
* sure to override `tryDecodeAccumulating` in order for fail-fast and
* accumulating decoding to be consistent.
*/
def tryDecode(c: ACursor): Decoder.Result[A] = c match {
case hc: HCursor => apply(hc)
case _ =>
Left(
DecodingFailure("Attempt to decode value on failed cursor", c.history)
)
}
def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[A] = c match {
case hc: HCursor => decodeAccumulating(hc)
case _ =>
Validated.invalidNel(
DecodingFailure("Attempt to decode value on failed cursor", c.history)
)
}
/**
* Decode the given [[Json]] value.
*/
final def decodeJson(j: Json): Decoder.Result[A] = apply(HCursor.fromJson(j))
final def accumulating: AccumulatingDecoder[A] = AccumulatingDecoder.fromDecoder(self)
/**
* Map a function over this [[Decoder]].
*/
final def map[B](f: A => B): Decoder[B] = new Decoder[B] {
final def apply(c: HCursor): Decoder.Result[B] = tryDecode(c)
override def tryDecode(c: ACursor): Decoder.Result[B] = self.tryDecode(c) match {
case Right(a) => Right(f(a))
case l @ Left(_) => l.asInstanceOf[Decoder.Result[B]]
}
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[B] =
tryDecodeAccumulating(c)
override final def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[B] =
self.tryDecodeAccumulating(c).map(f)
}
/**
* Monadically bind a function over this [[Decoder]].
*/
final def flatMap[B](f: A => Decoder[B]): Decoder[B] = new Decoder[B] {
final def apply(c: HCursor): Decoder.Result[B] = self(c) match {
case Right(a) => f(a)(c)
case l @ Left(_) => l.asInstanceOf[Decoder.Result[B]]
}
override def tryDecode(c: ACursor): Decoder.Result[B] = self.tryDecode(c) match {
case Right(a) => f(a).tryDecode(c)
case l @ Left(_) => l.asInstanceOf[Decoder.Result[B]]
}
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[B] =
self.decodeAccumulating(c).andThen(result => f(result).decodeAccumulating(c))
override final def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[B] =
self.tryDecodeAccumulating(c).andThen(result => f(result).tryDecodeAccumulating(c))
}
/**
* Create a new instance that handles any of this instance's errors with the
* given function.
*
* Note that in the case of accumulating decoding, only the first error will
* be used in recovery.
*/
final def handleErrorWith(f: DecodingFailure => Decoder[A]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Decoder.Result[A] =
Decoder.resultInstance.handleErrorWith(self(c))(failure => f(failure)(c))
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
AccumulatingDecoder.resultInstance.handleErrorWith(self.decodeAccumulating(c))(
failures => f(failures.head).decodeAccumulating(c)
)
}
/**
* Build a new instance with the specified error message.
*/
final def withErrorMessage(message: String): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Decoder.Result[A] = self(c) match {
case r @ Right(_) => r
case Left(e) => Left(e.withMessage(message))
}
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
self.decodeAccumulating(c).leftMap(_.map(_.withMessage(message)))
}
/**
* Build a new instance that fails if the condition does not hold for the
* result.
*
* Note that in the case of chained calls to this method, only the first
* failure will be returned.
*/
final def ensure(pred: A => Boolean, message: => String): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Decoder.Result[A] = self(c) match {
case r @ Right(a) => if (pred(a)) r else Left(DecodingFailure(message, c.history))
case l @ Left(_) => l
}
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] = self.decodeAccumulating(c) match {
case v @ Valid(a) => if (pred(a)) v else Validated.invalidNel(DecodingFailure(message, c.history))
case i @ Invalid(_) => i
}
}
/**
* Build a new instance that fails with one or more errors if the condition
* does not hold for the result.
*
* If the result of the function applied to the decoded value is the empty
* list, the new decoder will succeed with that value.
*/
final def ensure(errors: A => List[String]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Decoder.Result[A] = self(c) match {
case r @ Right(a) =>
errors(a) match {
case Nil => r
case message :: _ => Left(DecodingFailure(message, c.history))
}
case l @ Left(_) => l
}
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] = self.decodeAccumulating(c) match {
case v @ Valid(a) =>
errors(a).map(DecodingFailure(_, c.history)) match {
case Nil => v
case h :: t => Validated.invalid(NonEmptyList(h, t))
}
case i @ Invalid(_) => i
}
}
/**
* Build a new instance that fails if the condition does not hold for the
* input.
*
* Note that this condition is checked before decoding with the current
* decoder, and if it does not hold, decoding does not continue. This means
* that if you chain calls to this method, errors will not be accumulated
* (instead only the error of the last failing `validate` in the chain will be
* returned).
*/
final def validate(errors: HCursor => List[String]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Decoder.Result[A] =
errors(c).headOption.map { message =>
Left(DecodingFailure(message, c.history))
} getOrElse self(c)
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
errors(c).map(DecodingFailure(_, c.history)) match {
case Nil => self.decodeAccumulating(c)
case h :: t => Validated.invalid(NonEmptyList(h, t))
}
}
/**
* Build a new instance that fails if the condition does not hold for the
* input.
*
* Note that this condition is checked before decoding with the current
* decoder, and if it does not hold, decoding does not continue. This means
* that if you chain calls to this method, errors will not be accumulated
* (instead only the error of the last failing `validate` in the chain will be
* returned).
*/
final def validate(pred: HCursor => Boolean, message: => String): Decoder[A] = validate { c =>
if (pred(c)) Nil
else message :: Nil
}
/**
* Convert to a Kleisli arrow.
*/
final def kleisli: Kleisli[Decoder.Result, HCursor, A] =
Kleisli[Decoder.Result, HCursor, A](apply(_))
/**
* Run two decoders and return their results as a pair.
*/
final def product[B](fb: Decoder[B]): Decoder[(A, B)] = new Decoder[(A, B)] {
final def apply(c: HCursor): Decoder.Result[(A, B)] = Decoder.resultInstance.product(self(c), fb(c))
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[(A, B)] =
AccumulatingDecoder.resultInstance.product(self.decodeAccumulating(c), fb.decodeAccumulating(c))
}
/**
* Choose the first succeeding decoder.
*/
final def or[AA >: A](d: => Decoder[AA]): Decoder[AA] = new Decoder[AA] {
final def apply(c: HCursor): Decoder.Result[AA] = self(c) match {
case r @ Right(_) => r
case Left(_) => d(c)
}
}
/**
* Choose the first succeeding decoder, wrapping the result in a disjunction.
*/
final def either[B](decodeB: Decoder[B]): Decoder[Either[A, B]] = new Decoder[Either[A, B]] {
final def apply(c: HCursor): Decoder.Result[Either[A, B]] = self(c) match {
case Right(v) => Right(Left(v))
case Left(_) =>
decodeB(c) match {
case Right(v) => Right(Right(v))
case l @ Left(_) => l.asInstanceOf[Decoder.Result[Either[A, B]]]
}
}
}
/**
* Create a new decoder that performs some operation on the incoming JSON before decoding.
*/
final def prepare(f: ACursor => ACursor): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Decoder.Result[A] = tryDecode(c)
override def tryDecode(c: ACursor): Decoder.Result[A] = self.tryDecode(f(c))
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
tryDecodeAccumulating(c)
override def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[A] =
self.tryDecodeAccumulating(f(c))
}
/**
* Create a new decoder that performs some operation on the result if this one succeeds.
*
* @param f a function returning either a value or an error message
*/
final def emap[B](f: A => Either[String, B]): Decoder[B] = new Decoder[B] {
final def apply(c: HCursor): Decoder.Result[B] = tryDecode(c)
override def tryDecode(c: ACursor): Decoder.Result[B] =
self.tryDecode(c) match {
case Right(a) =>
f(a) match {
case r @ Right(_) => r.asInstanceOf[Decoder.Result[B]]
case Left(message) => Left(DecodingFailure(message, c.history))
}
case l @ Left(_) => l.asInstanceOf[Decoder.Result[B]]
}
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[B] =
tryDecodeAccumulating(c)
override final def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[B] =
self.tryDecodeAccumulating(c) match {
case Valid(a) =>
f(a) match {
case Right(b) => Validated.valid(b)
case Left(message) => Validated.invalidNel(DecodingFailure(message, c.history))
}
case l @ Invalid(_) => l.asInstanceOf[AccumulatingDecoder.Result[B]]
}
}
/**
* Create a new decoder that performs some operation on the result if this one succeeds.
*
* @param f a function returning either a value or an error message
*/
final def emapTry[B](f: A => Try[B]): Decoder[B] = new Decoder[B] {
final def apply(c: HCursor): Decoder.Result[B] = tryDecode(c)
override def tryDecode(c: ACursor): Decoder.Result[B] =
self.tryDecode(c) match {
case Right(a) =>
f(a) match {
case Success(b) => Right(b)
case Failure(t) => Left(DecodingFailure.fromThrowable(t, c.history))
}
case l @ Left(_) => l.asInstanceOf[Decoder.Result[B]]
}
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[B] =
tryDecodeAccumulating(c)
override final def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[B] =
self.tryDecodeAccumulating(c) match {
case Valid(a) =>
f(a) match {
case Success(b) => Validated.valid(b)
case Failure(t) => Validated.invalidNel(DecodingFailure.fromThrowable(t, c.history))
}
case l @ Invalid(_) => l.asInstanceOf[AccumulatingDecoder.Result[B]]
}
}
}
/**
* Utilities and instances for [[Decoder]].
*
* @groupname Aliases Type aliases
* @groupprio Aliases 0
*
* @groupname Utilities Defining decoders
* @groupprio Utilities 1
*
* @groupname Decoding General decoder instances
* @groupprio Decoding 2
*
* @groupname Collection Collection instances
* @groupprio Collection 3
*
* @groupname Disjunction Disjunction instances
* @groupdesc Disjunction Instance creation methods for disjunction-like types. Note that these
* instances are not implicit, since they require non-obvious decisions about the names of the
* discriminators. If you want instances for these types you can include the following import in
* your program:
* {{{
* import io.circe.disjunctionCodecs._
* }}}
* @groupprio Disjunction 4
*
* @groupname Instances Type class instances
* @groupprio Instances 5
*
* @groupname Tuple Tuple instances
* @groupprio Tuple 6
*
* @groupname Product Case class and other product instances
* @groupprio Product 7
*
* @groupname Time Java date and time instances
* @groupprio Time 8
*
* @groupname Prioritization Instance prioritization
* @groupprio Prioritization 10
*
* @author Travis Brown
*/
final object Decoder
extends CollectionDecoders
with TupleDecoders
with ProductDecoders
with JavaTimeDecoders
with LowPriorityDecoders {
/**
* @group Aliases
*/
type Result[A] = Either[DecodingFailure, A]
private[circe] val resultSemigroupK: SemigroupK[Result] = catsStdSemigroupKForEither[DecodingFailure]
private[this] abstract class DecoderWithFailure[A](name: String) extends Decoder[A] {
final def fail(c: HCursor): Result[A] = Left(DecodingFailure(name, c.history))
}
/**
* Return an instance for a given type.
*
* @group Utilities
*/
final def apply[A](implicit instance: Decoder[A]): Decoder[A] = instance
/**
* Create a decoder that always returns a single value, useful with some `flatMap` situations.
*
* @group Utilities
*/
final def const[A](a: A): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = Right(a)
final override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
Validated.valid(a)
}
/**
* Construct an instance from a function.
*
* @group Utilities
*/
final def instance[A](f: HCursor => Result[A]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = f(c)
}
/**
* Construct an instance from a [[cats.data.StateT]] value.
*
* @group Utilities
*/
def fromState[A](s: StateT[Result, ACursor, A]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = s.runA(c)
}
/**
* This is for easier interop with code that already returns [[scala.util.Try]]. You should
* prefer `instance` for any new code.
*
* @group Utilities
*/
final def instanceTry[A](f: HCursor => Try[A]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = f(c) match {
case Success(a) => Right(a)
case Failure(t) => Left(DecodingFailure.fromThrowable(t, c.history))
}
}
/**
* Construct an instance from a function that may reattempt on failure.
*
* @group Utilities
*/
final def withReattempt[A](f: ACursor => Result[A]): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = tryDecode(c)
override def tryDecode(c: ACursor): Decoder.Result[A] = f(c)
override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] = tryDecodeAccumulating(c)
override def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[A] = f(c) match {
case Right(v) => Validated.valid(v)
case Left(e) => Validated.invalidNel(e)
}
}
/**
* Construct an instance that always fails with the given [[DecodingFailure]].
*
* @group Utilities
*/
final def failed[A](failure: DecodingFailure): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = Left(failure)
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
Validated.invalidNel(failure)
}
/**
* Construct an instance that always fails with the given error message.
*
* @group Utilities
*/
final def failedWithMessage[A](message: String): Decoder[A] = new Decoder[A] {
final def apply(c: HCursor): Result[A] = Left(DecodingFailure(message, c.history))
override final def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[A] =
Validated.invalidNel(DecodingFailure(message, c.history))
}
/**
* @group Decoding
*/
implicit final val decodeHCursor: Decoder[HCursor] = new Decoder[HCursor] {
final def apply(c: HCursor): Result[HCursor] = Right(c)
}
/**
* @group Decoding
*/
implicit final val decodeJson: Decoder[Json] = new Decoder[Json] {
final def apply(c: HCursor): Result[Json] = Right(c.value)
}
/**
* @group Decoding
*/
implicit final val decodeJsonObject: Decoder[JsonObject] = new Decoder[JsonObject] {
final def apply(c: HCursor): Result[JsonObject] = c.value.asObject match {
case Some(v) => Right(v)
case None => Left(DecodingFailure("JsonObject", c.history))
}
}
/**
* @group Decoding
*/
implicit final val decodeJsonNumber: Decoder[JsonNumber] = new Decoder[JsonNumber] {
final def apply(c: HCursor): Result[JsonNumber] = c.value.asNumber match {
case Some(v) => Right(v)
case None => Left(DecodingFailure("JsonNumber", c.history))
}
}
/**
* @group Decoding
*/
implicit final val decodeString: Decoder[String] = new Decoder[String] {
final def apply(c: HCursor): Result[String] = c.value match {
case Json.JString(string) => Right(string)
case _ => Left(DecodingFailure("String", c.history))
}
}
/**
* @group Decoding
*/
implicit final val decodeUnit: Decoder[Unit] = new Decoder[Unit] {
final def apply(c: HCursor): Result[Unit] = c.value match {
case Json.JObject(obj) if obj.isEmpty => Right(())
case Json.JArray(arr) if arr.isEmpty => Right(())
case other if other.isNull => Right(())
case _ => Left(DecodingFailure("Unit", c.history))
}
}
/**
* @group Decoding
*/
implicit final val decodeBoolean: Decoder[Boolean] = new Decoder[Boolean] {
final def apply(c: HCursor): Result[Boolean] = c.value match {
case Json.JBoolean(b) => Right(b)
case _ => Left(DecodingFailure("Boolean", c.history))
}
}
/**
* Decode a JSON value into a `java.lang.Boolean`.
*
* @group Decoding
*/
implicit final val decodeJavaBoolean: Decoder[java.lang.Boolean] = decodeBoolean.map(java.lang.Boolean.valueOf)
/**
* @group Decoding
*/
implicit final val decodeChar: Decoder[Char] = new Decoder[Char] {
final def apply(c: HCursor): Result[Char] = c.value match {
case Json.JString(string) if string.length == 1 => Right(string.charAt(0))
case _ => Left(DecodingFailure("Char", c.history))
}
}
/**
* Decode a JSON value into a `java.lang.Character`.
*
* @group Decoding
*/
implicit final val decodeJavaCharacter: Decoder[java.lang.Character] = decodeChar.map(java.lang.Character.valueOf)
/**
* Decode a JSON value into a [[scala.Float]].
*
* See [[decodeDouble]] for discussion of the approach taken for floating-point decoding.
*
* @group Decoding
*/
implicit final val decodeFloat: Decoder[Float] = new DecoderWithFailure[Float]("Float") {
final def apply(c: HCursor): Result[Float] = c.value match {
case Json.JNumber(number) => Right(number.toDouble.toFloat)
case Json.JString(string) =>
JsonNumber.fromString(string).map(_.toDouble.toFloat) match {
case Some(v) => Right(v)
case None => fail(c)
}
case other if other.isNull => Right(Float.NaN)
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.lang.Float`.
*
* @group Decoding
*/
implicit final val decodeJavaFloat: Decoder[java.lang.Float] = decodeFloat.map(java.lang.Float.valueOf)
/**
* Decode a JSON value into a [[scala.Double]].
*
* Unlike the integral decoders provided here, this decoder will accept values that are too large
* to be represented and will return them as `PositiveInfinity` or `NegativeInfinity`, and it may
* lose precision.
*
* @group Decoding
*/
implicit final val decodeDouble: Decoder[Double] = new DecoderWithFailure[Double]("Double") {
final def apply(c: HCursor): Result[Double] = c.value match {
case Json.JNumber(number) => Right(number.toDouble)
case Json.JString(string) =>
JsonNumber.fromString(string).map(_.toDouble) match {
case Some(v) => Right(v)
case None => fail(c)
}
case other if other.isNull => Right(Double.NaN)
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.lang.Double`.
*
* @group Decoding
*/
implicit final val decodeJavaDouble: Decoder[java.lang.Double] = decodeDouble.map(java.lang.Double.valueOf)
/**
* Decode a JSON value into a [[scala.Byte]].
*
* See [[decodeLong]] for discussion of the approach taken for integral decoding.
*
* @group Decoding
*/
implicit final val decodeByte: Decoder[Byte] = new DecoderWithFailure[Byte]("Byte") {
final def apply(c: HCursor): Result[Byte] = c.value match {
case Json.JNumber(number) =>
number.toByte match {
case Some(v) => Right(v)
case None => fail(c)
}
case Json.JString(string) =>
JsonNumber.fromString(string).flatMap(_.toByte) match {
case Some(value) => Right(value)
case None => fail(c)
}
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.lang.Byte`.
*
* @group Decoding
*/
implicit final val decodeJavaByte: Decoder[java.lang.Byte] = decodeByte.map(java.lang.Byte.valueOf)
/**
* Decode a JSON value into a [[scala.Short]].
*
* See [[decodeLong]] for discussion of the approach taken for integral decoding.
*
* @group Decoding
*/
implicit final val decodeShort: Decoder[Short] = new DecoderWithFailure[Short]("Short") {
final def apply(c: HCursor): Result[Short] = c.value match {
case Json.JNumber(number) =>
number.toShort match {
case Some(v) => Right(v)
case None => fail(c)
}
case Json.JString(string) =>
JsonNumber.fromString(string).flatMap(_.toShort) match {
case Some(value) => Right(value)
case None => fail(c)
}
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.lang.Short`.
*
* @group Decoding
*/
implicit final val decodeJavaShort: Decoder[java.lang.Short] = decodeShort.map(java.lang.Short.valueOf)
/**
* Decode a JSON value into a [[scala.Int]].
*
* See [[decodeLong]] for discussion of the approach taken for integral decoding.
*
* @group Decoding
*/
implicit final val decodeInt: Decoder[Int] = new DecoderWithFailure[Int]("Int") {
final def apply(c: HCursor): Result[Int] = c.value match {
case Json.JNumber(number) =>
number.toInt match {
case Some(v) => Right(v)
case None => fail(c)
}
case Json.JString(string) =>
JsonNumber.fromString(string).flatMap(_.toInt) match {
case Some(value) => Right(value)
case None => fail(c)
}
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.lang.Integer`.
*
* @group Decoding
*/
implicit final val decodeJavaInteger: Decoder[java.lang.Integer] = decodeInt.map(java.lang.Integer.valueOf)
/**
* Decode a JSON value into a [[scala.Long]].
*
* Decoding will fail if the value doesn't represent a whole number within the range of the target
* type (although it can have a decimal part: e.g. `10.0` will be successfully decoded, but
* `10.01` will not). If the value is a JSON string, the decoder will attempt to parse it as a
* number.
*
* @group Decoding
*/
implicit final val decodeLong: Decoder[Long] = new DecoderWithFailure[Long]("Long") {
final def apply(c: HCursor): Result[Long] = c.value match {
case Json.JNumber(number) =>
number.toLong match {
case Some(v) => Right(v)
case None => fail(c)
}
case Json.JString(string) =>
JsonNumber.fromString(string).flatMap(_.toLong) match {
case Some(value) => Right(value)
case None => fail(c)
}
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.lang.Long`.
*
* @group Decoding
*/
implicit final val decodeJavaLong: Decoder[java.lang.Long] = decodeLong.map(java.lang.Long.valueOf)
/**
* Decode a JSON value into a [[scala.math.BigInt]].
*
* Note that decoding will fail if the number has a large number of digits (the limit is currently
* `1 << 18`, or around a quarter million). Larger numbers can be decoded by mapping over a
* [[scala.math.BigDecimal]], but be aware that the conversion to the integral form can be
* computationally expensive.
*
* @group Decoding
*/
implicit final val decodeBigInt: Decoder[BigInt] = new DecoderWithFailure[BigInt]("BigInt") {
final def apply(c: HCursor): Result[BigInt] = c.value match {
case Json.JNumber(number) =>
number.toBigInt match {
case Some(v) => Right(v)
case None => fail(c)
}
case Json.JString(string) =>
JsonNumber.fromString(string).flatMap(_.toBigInt) match {
case Some(value) => Right(value)
case None => fail(c)
}
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.math.BigInteger`.
*
* @group Decoding
*/
implicit final val decodeJavaBigInteger: Decoder[java.math.BigInteger] = decodeBigInt.map(_.bigInteger)
/**
* Decode a JSON value into a [[scala.math.BigDecimal]].
*
* Note that decoding will fail on some very large values that could in principle be represented
* as `BigDecimal`s (specifically if the `scale` is out of the range of `scala.Int` when the
* `unscaledValue` is adjusted to have no trailing zeros). These large values can, however, be
* round-tripped through `JsonNumber`, so you may wish to use [[decodeJsonNumber]] in these cases.
*
* Also note that because `scala.scalajs.js.JSON` parses JSON numbers into a floating point
* representation, decoding a JSON number into a `BigDecimal` on Scala.js may lose precision.
*
* @group Decoding
*/
implicit final val decodeBigDecimal: Decoder[BigDecimal] = new DecoderWithFailure[BigDecimal]("BigDecimal") {
final def apply(c: HCursor): Result[BigDecimal] = c.value match {
case Json.JNumber(number) =>
number.toBigDecimal match {
case Some(v) => Right(v)
case None => fail(c)
}
case Json.JString(string) =>
JsonNumber.fromString(string).flatMap(_.toBigDecimal) match {
case Some(value) => Right(value)
case None => fail(c)
}
case _ => fail(c)
}
}
/**
* Decode a JSON value into a `java.math.BigDecimal`.
*
* @group Decoding
*/
implicit final val decodeJavaBigDecimal: Decoder[java.math.BigDecimal] = decodeBigDecimal.map(_.bigDecimal)
/**
* @group Decoding
*/
implicit final val decodeUUID: Decoder[UUID] = new Decoder[UUID] {
private[this] def fail(c: HCursor): Result[UUID] = Left(DecodingFailure("UUID", c.history))
final def apply(c: HCursor): Result[UUID] = c.value match {
case Json.JString(string) if string.length == 36 =>
try Right(UUID.fromString(string))
catch {
case _: IllegalArgumentException => fail(c)
}
case _ => fail(c)
}
}
private[this] final val rightNone: Either[DecodingFailure, Option[Nothing]] = Right(None)
private[circe] final val keyMissingNone: Decoder.Result[Option[Nothing]] = Right(None)
private[circe] final val keyMissingNoneAccumulating: AccumulatingDecoder.Result[Option[Nothing]] =
Validated.valid(None)
/**
* @group Decoding
*/
implicit final def decodeOption[A](implicit d: Decoder[A]): Decoder[Option[A]] = new Decoder[Option[A]] {
final def apply(c: HCursor): Result[Option[A]] = tryDecode(c)
final override def tryDecode(c: ACursor): Decoder.Result[Option[A]] = c match {
case c: HCursor =>
if (c.value.isNull) rightNone
else
d(c) match {
case Right(a) => Right(Some(a))
case Left(df) => Left(df)
}
case c: FailedCursor =>
if (!c.incorrectFocus) keyMissingNone else Left(DecodingFailure("[A]Option[A]", c.history))
}
final override def decodeAccumulating(c: HCursor): AccumulatingDecoder.Result[Option[A]] = tryDecodeAccumulating(c)
final override def tryDecodeAccumulating(c: ACursor): AccumulatingDecoder.Result[Option[A]] = {
val result = tryDecode(c)
if (result eq keyMissingNone) {
keyMissingNoneAccumulating
} else {
result match {
case Right(v) => Validated.valid(v)
case Left(e) => Validated.invalidNel(e)
}
}
}
}
/**
* @group Decoding
*/
implicit final def decodeSome[A](implicit d: Decoder[A]): Decoder[Some[A]] = d.map(Some(_))
/**
* @group Decoding
*/
implicit final val decodeNone: Decoder[None.type] = new Decoder[None.type] {
final def apply(c: HCursor): Result[None.type] = if (c.value.isNull) Right(None)
else {
Left(DecodingFailure("None", c.history))
}
}
/**
* @group Collection
*/
implicit final def decodeMap[K, V](
implicit
decodeK: KeyDecoder[K],
decodeV: Decoder[V]
): Decoder[ImmutableMap[K, V]] = new MapDecoder[K, V, ImmutableMap](decodeK, decodeV) {
final protected def createBuilder(): Builder[(K, V), ImmutableMap[K, V]] = ImmutableMap.newBuilder[K, V]
}
/**
* @group Collection
*/
implicit final def decodeSeq[A](implicit decodeA: Decoder[A]): Decoder[Seq[A]] = new SeqDecoder[A, Seq](decodeA) {
final protected def createBuilder(): Builder[A, Seq[A]] = Seq.newBuilder[A]
}
/**
* @group Collection
*/
implicit final def decodeSet[A](implicit decodeA: Decoder[A]): Decoder[Set[A]] = new SeqDecoder[A, Set](decodeA) {
final protected def createBuilder(): Builder[A, Set[A]] = Set.newBuilder[A]
}
/**
* @group Collection
*/
implicit final def decodeList[A](implicit decodeA: Decoder[A]): Decoder[List[A]] = new SeqDecoder[A, List](decodeA) {
final protected def createBuilder(): Builder[A, List[A]] = List.newBuilder[A]
}
/**
* @group Collection
*/
implicit final def decodeVector[A](implicit decodeA: Decoder[A]): Decoder[Vector[A]] =
new SeqDecoder[A, Vector](decodeA) {
final protected def createBuilder(): Builder[A, Vector[A]] = Vector.newBuilder[A]
}
private[this] class ChainBuilder[A] extends CompatBuilder[A, Chain[A]] {
private[this] var xs: Chain[A] = Chain.nil
final def clear(): Unit = xs = Chain.nil
final def result(): Chain[A] = xs
final def addOne(elem: A): this.type = {
xs = xs.append(elem)
this
}
}
/**
* @group Collection
*/
implicit final def decodeChain[A](implicit decodeA: Decoder[A]): Decoder[Chain[A]] =
new SeqDecoder[A, Chain](decodeA) {
final protected def createBuilder(): Builder[A, Chain[A]] = new ChainBuilder[A]
}
/**
* @group Collection
*/
implicit final def decodeNonEmptyList[A](implicit decodeA: Decoder[A]): Decoder[NonEmptyList[A]] =
new NonEmptySeqDecoder[A, List, NonEmptyList[A]](decodeA) {
final protected def createBuilder(): Builder[A, List[A]] = List.newBuilder[A]
final protected val create: (A, List[A]) => NonEmptyList[A] = (h, t) => NonEmptyList(h, t)
}
/**
* @group Collection
*/
implicit final def decodeNonEmptyVector[A](implicit decodeA: Decoder[A]): Decoder[NonEmptyVector[A]] =
new NonEmptySeqDecoder[A, Vector, NonEmptyVector[A]](decodeA) {
final protected def createBuilder(): Builder[A, Vector[A]] = Vector.newBuilder[A]
final protected val create: (A, Vector[A]) => NonEmptyVector[A] = (h, t) => NonEmptyVector(h, t)
}
/**
* @group Collection
*/
implicit final def decodeNonEmptySet[A](implicit decodeA: Decoder[A], orderA: Order[A]): Decoder[NonEmptySet[A]] =
new NonEmptySeqDecoder[A, SortedSet, NonEmptySet[A]](decodeA) {
final protected def createBuilder(): Builder[A, SortedSet[A]] =
SortedSet.newBuilder[A](Order.catsKernelOrderingForOrder(orderA))
final protected val create: (A, SortedSet[A]) => NonEmptySet[A] =
(h, t) => NonEmptySet(h, t)
}
/**
* @group Collection
*/
implicit final def decodeNonEmptyMap[K, V](
implicit
decodeK: KeyDecoder[K],
orderK: Order[K],
decodeV: Decoder[V]
): Decoder[NonEmptyMap[K, V]] =
new MapDecoder[K, V, SortedMap](decodeK, decodeV) {
final protected def createBuilder(): Builder[(K, V), SortedMap[K, V]] =
SortedMap.newBuilder[K, V](Order.catsKernelOrderingForOrder(orderK))
}.emap { map =>
NonEmptyMap.fromMap(map)(orderK).toRight("[K, V]NonEmptyMap[K, V]")
}
/**
* @group Collection
*/
implicit final def decodeNonEmptyChain[A](implicit decodeA: Decoder[A]): Decoder[NonEmptyChain[A]] =
new NonEmptySeqDecoder[A, Chain, NonEmptyChain[A]](decodeA) {
final protected def createBuilder(): Builder[A, Chain[A]] = new ChainBuilder[A]
final protected val create: (A, Chain[A]) => NonEmptyChain[A] =
(h, t) => NonEmptyChain.fromChainPrepend(h, t)
}
/**
* @group Disjunction
*/
final def decodeEither[A, B](leftKey: String, rightKey: String)(
implicit
da: Decoder[A],
db: Decoder[B]
): Decoder[Either[A, B]] = new Decoder[Either[A, B]] {
final def apply(c: HCursor): Result[Either[A, B]] = {
val lf = c.downField(leftKey)
val rf = c.downField(rightKey)
lf match {
case lc: HCursor =>
rf match {
case rc: HCursor => Left(DecodingFailure("[A, B]Either[A, B]", c.history))
case rc =>
da(lc) match {
case Right(v) => Right(Left(v))
case l @ Left(_) => l.asInstanceOf[Result[Either[A, B]]]
}
}
case lc =>
rf match {
case rc: HCursor =>
db(rc) match {
case Right(v) => Right(Right(v))
case l @ Left(_) => l.asInstanceOf[Result[Either[A, B]]]
}
case rc => Left(DecodingFailure("[A, B]Either[A, B]", c.history))
}
}
}
}
/**
* @group Disjunction
*/
final def decodeValidated[E, A](failureKey: String, successKey: String)(
implicit
de: Decoder[E],
da: Decoder[A]
): Decoder[Validated[E, A]] =
decodeEither[E, A](
failureKey,
successKey
).map(Validated.fromEither).withErrorMessage("[E, A]Validated[E, A]")
/**
* @group Instances
*/
final val resultInstance: MonadError[Result, DecodingFailure] = catsStdInstancesForEither[DecodingFailure]
/**
* @group Instances
*/
implicit final val decoderInstances: SemigroupK[Decoder] with MonadError[Decoder, DecodingFailure] =
new SemigroupK[Decoder] with MonadError[Decoder, DecodingFailure] {
final def combineK[A](x: Decoder[A], y: Decoder[A]): Decoder[A] = x.or(y)
final def pure[A](a: A): Decoder[A] = const(a)
override final def map[A, B](fa: Decoder[A])(f: A => B): Decoder[B] = fa.map(f)
override final def product[A, B](fa: Decoder[A], fb: Decoder[B]): Decoder[(A, B)] = fa.product(fb)
final def flatMap[A, B](fa: Decoder[A])(f: A => Decoder[B]): Decoder[B] = fa.flatMap(f)
final def raiseError[A](e: DecodingFailure): Decoder[A] = Decoder.failed(e)
final def handleErrorWith[A](fa: Decoder[A])(f: DecodingFailure => Decoder[A]): Decoder[A] = fa.handleErrorWith(f)
final def tailRecM[A, B](a: A)(f: A => Decoder[Either[A, B]]): Decoder[B] = new Decoder[B] {
@tailrec
private[this] def step(c: HCursor, a1: A): Result[B] = f(a1)(c) match {
case l @ Left(_) => l.asInstanceOf[Result[B]]
case Right(Left(a2)) => step(c, a2)
case Right(Right(b)) => Right(b)
}
final def apply(c: HCursor): Result[B] = step(c, a)
}
}
/**
* {{{
* object WeekDay extends Enumeration { ... }
* implicit val weekDayDecoder = Decoder.enumDecoder(WeekDay)
* }}}
*
* @group Utilities
*/
final def enumDecoder[E <: Enumeration](enum: E): Decoder[E#Value] =
Decoder.decodeString.flatMap { str =>
Decoder.instanceTry { _ =>
Try(enum.withName(str))
}
}
/**
* Helper methods for working with [[cats.data.StateT]] values that transform
* the [[ACursor]].
*
* @group Utilities
*/
final object state {
/**
* Attempt to decode a value at key `k` and remove it from the [[ACursor]].
*/
def decodeField[A: Decoder](k: String): StateT[Result, ACursor, A] = StateT[Result, ACursor, A] { c =>
val field = c.downField(k)
field.as[A] match {
case Right(a) if field.failed => Right((c, a))
case Right(a) => Right((field.delete, a))
case l @ Left(_) => l.asInstanceOf[Result[(ACursor, A)]]
}
}
/**
* Require the [[ACursor]] to be empty, using the provided function to
* create the failure error message if it's not.
*/
def requireEmptyWithMessage(createMessage: List[String] => String): StateT[Result, ACursor, Unit] =
StateT[Result, ACursor, Unit] { c =>
val keys = c.focus.flatMap(_.asObject).toList.flatMap(_.keys)
if (keys.isEmpty) Right((c, ())) else Left(DecodingFailure(createMessage(keys), c.history))
}
/**
* Require the [[ACursor]] to be empty, with a default message.
*/
val requireEmpty: StateT[Result, ACursor, Unit] = requireEmptyWithMessage { keys =>
s"Leftover keys: ${keys.mkString(", ")}"
}
}
}
private[circe] trait LowPriorityDecoders {
/**
* @group Prioritization
*/
implicit def importedDecoder[A](implicit exported: Exported[Decoder[A]]): Decoder[A] = exported.instance
}