Skip to content

Instantly share code, notes, and snippets.

@gbougeard
Created May 28, 2015 15:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gbougeard/12f41a4036e22b1b0a21 to your computer and use it in GitHub Desktop.
Save gbougeard/12f41a4036e22b1b0a21 to your computer and use it in GitHub Desktop.
import play.api.data.mapping.{Failure, Success, Rule, Write}
import play.api.libs.json.{Json, JsValue}
import spray.httpx.marshalling.Marshaller
import spray.httpx.unmarshalling.{ Deserialized, MalformedContent, SimpleUnmarshaller, Unmarshaller }
import spray.http._
import MediaTypes._
/**
* A trait providing automatic to and from JSON marshalling/unmarshalling using in-scope *play-json* Reads/Writes.
* Note that *spray-httpx* does not have an automatic dependency on *play-json*.
* You'll need to provide the appropriate *play-json* artifacts yourself.
*/
trait ValidationJsonSupport {
type R[T] = Rule[JsValue, T]
type W[T] = Write[T, JsValue]
implicit def validationJsonUnmarshaller[T: R] =
delegate[String, T](`application/json`)(string ⇒
implicitly[R[T]].validate(Json.parse(string)) match {
case Success(s) => Right(s)
case Failure(e) => Left(MalformedContent(s"Received JSON is not valid.\n${e}"))
}
// try {
// implicitly[R[T]].validate(Json.parse(string)).asEither.left.map(e ⇒ MalformedContent(s"Received JSON is not valid.\n${Json.prettyPrint(JsError.toFlatJson(e))}"))
// } catch {
// case NonFatal(exc) ⇒ Left(MalformedContent(exc.getMessage, exc))
// }
)(UTF8StringUnmarshaller)
implicit def validationJsonMarshaller[T: W](implicit printer: JsValue ⇒ String = Json.stringify) =
Marshaller.delegate[T, String](ContentTypes.`application/json`) { value ⇒
printer(implicitly[W[T]].writes(value))
}
//
private val UTF8StringUnmarshaller = new Unmarshaller[String] {
def apply(entity: HttpEntity) = Right(entity.asString(defaultCharset = HttpCharsets.`UTF-8`))
}
// Unmarshaller.delegate is used as a kind of map operation; play-json JsResult can contain either validation errors or the JsValue
// representing a JSON object. We need a delegate method that works as a flatMap and let the provided A ⇒ Deserialized[B] function
// to deal with any possible error, including exceptions.
//
private def delegate[A, B](unmarshalFrom: ContentTypeRange*)(f: A ⇒ Deserialized[B])(implicit ma: Unmarshaller[A]): Unmarshaller[B] =
new SimpleUnmarshaller[B] {
val canUnmarshalFrom = unmarshalFrom
def unmarshal(entity: HttpEntity) = ma(entity).right.flatMap(a ⇒ f(a))
}
}
object ValidationJsonSupport extends ValidationJsonSupport
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment