Skip to content

Instantly share code, notes, and snippets.

@mikemckibben
Last active June 28, 2017 17:19
Show Gist options
  • Save mikemckibben/fad4328de85a79a06bf3 to your computer and use it in GitHub Desktop.
Save mikemckibben/fad4328de85a79a06bf3 to your computer and use it in GitHub Desktop.
Example Unmarshalling an Either[A, B] with spray-json
import org.specs2.mutable.Specification
import spray.http.{HttpResponse, StatusCodes}
import spray.httpx.SprayJsonSupport
import spray.httpx.marshalling._
import spray.httpx.unmarshalling._
import spray.json._
/***
* scalaVersion := "2.11.2"
*
* resolvers += "spray repo" at "http://repo.spray.io"
*
* libraryDependencies ++= Seq(
* "com.typesafe.akka" %% "akka-actor" % "2.3.5",
* "com.typesafe.akka" %% "akka-slf4j" % "2.3.5",
* "com.typesafe.akka" %% "akka-testkit" % "2.3.5" % "test",
* "org.apache.logging.log4j" % "log4j-api" % "2.0.2",
* "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.0.2",
* "org.apache.logging.log4j" % "log4j-jcl" % "2.0.2",
* "org.slf4j" % "slf4j-api" % "1.7.7",
* "io.spray" %% "spray-can" % "1.3.1",
* "io.spray" %% "spray-routing" % "1.3.1",
* "io.spray" %% "spray-json" % "1.2.6",
* "io.spray" %% "spray-testkit" % "1.3.1" % "test",
* "org.specs2" %% "specs2" % "2.4.1" % "test"
* )
*/
class EitherExampleSpec extends Specification with SprayJsonSupport with DefaultJsonProtocol {
object Data {
case class Success[A](result: A)
object Success {
implicit def successFormat[A](implicit format: JsonFormat[A]) = new RootJsonFormat[Success[A]] {
override def write(value: Success[A]): JsValue = {
JsObject("ok" -> JsBoolean(true), "result" -> format.write(value.result))
}
override def read(json: JsValue): Success[A] = {
val root = json.asJsObject
(root.fields.get("ok"), root.fields.get("result")) match {
case (Some(JsTrue), Some(jsValue)) => Success(format.read(jsValue))
case _ => deserializationError("JSON not a Success")
}
}
}
}
case class Failure(reason: String)
object Failure {
implicit object failureFormat extends RootJsonFormat[Failure] {
override def write(value: Failure): JsValue = {
JsObject("ok" -> JsBoolean(false), "error" -> JsString(value.reason))
}
override def read(json: JsValue): Failure = {
val root = json.asJsObject
(root.fields.get("ok"), root.fields.get("error")) match {
case (Some(JsFalse), Some(JsString(reason))) => Failure(reason)
case _ => deserializationError("JSON not a Failure")
}
}
}
}
type Result[A] = Either[Failure, Success[A]]
implicit def rootEitherFormat[A : RootJsonFormat, B : RootJsonFormat] = new RootJsonFormat[Either[A, B]] {
val format = DefaultJsonProtocol.eitherFormat[A, B]
def write(either: Either[A, B]) = format.write(either)
def read(value: JsValue) = format.read(value)
}
}
"Example Unmarshalling JSON Response as Either[Failure, Success]" >> {
import Data._
"unmarshalling a Success[String] json response" in {
val result: Result[String] = Right(Success("Success!"))
val jsonResponse = HttpResponse(status = StatusCodes.OK, entity = marshalUnsafe(result))
jsonResponse.as[Result[String]] must beRight(Right(Success("Success!")))
}
"unmarshalling a Failure json response" in {
val result: Result[String] = Left(Failure("Failure!"))
val jsonResponse = HttpResponse(status = StatusCodes.OK, entity = marshalUnsafe(result))
jsonResponse.as[Result[String]] must beRight(Left(Failure("Failure!")))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment