Reputation: 3197
I am trying to change the implementation of this function from using plays json library like so
def apply[T](action: => ApiResponse[T])(implicit tjs: Writes[T], ec: ExecutionContext): Future[Result] = {
action.fold(
err =>
Status(err.statusCode) {
JsObject(Seq(
"status" -> JsString("error"),
"statusCode" -> JsNumber(err.statusCode),
"errors" -> Json.toJson(err.errors)
))
},
t =>
Ok {
JsObject(Seq(
"status" -> JsString("ok"),
"response" -> Json.toJson(t)
))
}
)
}
to use argonaut like so
def apply[T](action: => ApiResponse[T])(implicit encodeJson: EncodeJson[T], ec: ExecutionContext): Future[Result] = {
action.fold(
err =>
Status(err.statusCode) {
Json(
"status" -> jString("error"),
"statusCode" -> jNumber(err.statusCode),
"errors" -> err.errors.asJson
)
},
t =>
Ok {
Json(
"status" -> jString("ok"),
"response" -> t.asJson
)
}
)
}
but I get
Cannot write an instance of argonaut.Json to HTTP response. Try to define a Writeable[argonaut.Json]
for both the Status{} block and Ok{} block and I got a helpful answer to this problem here https://groups.google.com/forum/#!topic/play-framework/vBMf72a10Zc
so I tried to create the implicit conversion like so
implicit def writeableOfArgonautJson(implicit codec: Codec): Writeable[Json] = {
Writeable(jsval => codec.encode(jsval.toString))
}
which I think converts the json object to a string and provides it to codec.encode which should convert it to Array[Bytes] but I get
Cannot guess the content type to use for argonaut.Json. Try to define a ContentTypeOf[argonaut.Json]
jsval.nospaces.getBytes also return Array[Bytes] so I dont know if that can be used to help at all
so while I think that last error message means I just need to tell play that it should use content type application.json I also feel like this might be an unnecessary rabbit hole and there should be an easier way to do this.
edit: it wasnt such a rabbit hole as defining the contentType has things compiling at least but I still would like to know if this is correct
Upvotes: 4
Views: 2301
Reputation: 8901
You seem to have answered your own question, but to confirm, the Writable[A]
is:
A
to Array[Bytes]
andThe character encoding is taken care of by the implicit Codec
instance, so you then need an implicit ContentTypeOf[A]
where A
is argonaunt.Json
:
implicit def contentTypeOf_ArgonautJson(implicit codec: Codec): ContentTypeOf[argonaut.Json] = {
ContentTypeOf[argonaut.Json](Some(ContentTypes.JSON))
}
and then the Writable[A]
, which has a type constraint on A
that there be an in-scope ContentTypeOf[A]
(which you've just defined):
implicit def writeableOf_ArgonautJson(implicit codec: Codec): Writeable[argonaut.Json] = {
Writeable(jsval => codec.encode(jsval.toString))
}
And as you point out, there endeth the rabbit hole. And indeed, it certainly seems a bit of a diversion but not much extra code when you consider you can now just do Ok(myArgonautObject)
in as many actions as you want without further conversion and header-setting boilerplate.
Perhaps you could put those implicits in an ExtraJsonHelpers
trait and mix it in to your controllers, for more boilerplate reduction.
Upvotes: 3