Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider adding a BodyWritable[Json] type class instance #36

Open
tbinna opened this issue Aug 16, 2017 · 6 comments
Open

Consider adding a BodyWritable[Json] type class instance #36

tbinna opened this issue Aug 16, 2017 · 6 comments

Comments

@tbinna
Copy link

tbinna commented Aug 16, 2017

With the update to Play 2.6 my Play WS clients broke.

Previously body serialization when calling put, post, etc. worked using play.api.http.Writable and type class instances writableOf_Json in Circe.scala did the job.

Now play-ws requires a type class play.api.libs.ws.BodyWritable which is not included in this project. See https://www.playframework.com/documentation/2.6.x/WSMigration26#Scala

For now I defined this trait in my project which does the job:

trait CirceJsonBodyWritables {

  val defaultPrinter = Printer.noSpaces

  implicit def bodyWritableOf_Json(
      implicit printer: Printer = defaultPrinter): BodyWritable[Json] = {
    BodyWritable(
      json => InMemoryBody(ByteString.fromString(json.pretty(printer))),
      "application/json")
  }

}

Would be nice to add this to the Circe.scala trait but it requires you to add play-ws dependency.

@tbinna tbinna changed the title Consider adding a BodyWritable[Json] Consider adding a BodyWritable[Json] type class instance Aug 16, 2017
@jilen
Copy link
Owner

jilen commented Aug 17, 2017

@tbinna Circe trait is basically for Controller, BodyWritable instances seems should be also available somewhere else

@tbinna
Copy link
Author

tbinna commented Aug 18, 2017

Yes, that's true. It could be a different trait for Play WS. For Play 2.5.x I used the same Circe trait for my WS clients. This worked because play-ws was using the same Writable to serialize bodies. Now with Play 2.6 I still use the Circe trait but I need to define a BodyWritable[io.circe.Json] for WS clients.
If I find some time I will create PR, then we can discuss based on that.

@romanskie
Copy link

romanskie commented Jan 25, 2018

Hey Guys,

so is there by now a way or workaround to parse WSReponse bodys to case classes? I tried to write a custom CirceJsonBodyReadables trait by myself, but that did not work out. Maybe I am using it wrong, but I cant just use @tbinna writable trait for that right?

trait CirceJsonBodyReadables {

  implicit val circeBodyReadable = BodyReadable[io.circe.Json] { response =>
    import play.shaded.ahc.org.asynchttpclient.{ Response => AHCResponse }
    val ahcResponse = response.asInstanceOf[StandaloneAhcWSResponse].underlying[AHCResponse]
    io.circe.Json.fromString(ahcResponse.getResponseBody)
  }
}

In with my response I am trying to do something like.

wsClient.url(btcURL).get().map { response => response.body[io.circe.Json] }

But that leads to the following error message:

An error has occured: play.api.libs.ws.ahc.AhcWSResponse cannot be cast to play.api.libs.ws.ahc.StandaloneAhcWSResponse

An ugly solution code be to map the body as String and parse it manually?
I would like to help integrating that in play-circe!

@jilen
Copy link
Owner

jilen commented Jan 26, 2018

@romanskie I think it would be better not casting to StandaloneAhcWSResponse
but relies on WSResponse.

@jilen
Copy link
Owner

jilen commented Jan 26, 2018

@romanskie Also you may define

implicit def circeBodyReadable[A: io.circe.Decoder] = BodyReadable[io.circe.Decoder.Result[A]]] { response =>
   decode[A](response.body)
}

Which will enable parse response to Decoder.Result[A]

@romanskie
Copy link

romanskie commented Jan 26, 2018

I wrote this now:

 implicit def circeJsonBodyReadable = BodyReadable[io.circe.Json] { response =>
    io.circe.parser.parse(response.bodyAsBytes.utf8String) match {
      case Left(decodingFailure) => throw decodingFailure
      case Right(json) => json
    }
  }

And now I am able to:

wsClient.url(btcURL).get().map(response => response.body[Json].as[CryptoCompareResponse])

That was the only solution I could apply. It would be very nice if I just could use the following:

wsClient.url(btcURL).get().map(response => response.body[CryptoCompareResponse])

I guess that is what you wanted me to do with your implicit def, but i could not get it to work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants