RosHTTP icon indicating copy to clipboard operation
RosHTTP copied to clipboard

Use json4s

Open hmil opened this issue 9 years ago • 9 comments

Having our own JSON ast implementation is redundant and for the sake of preserving human sanity we should use json4s instead.

hmil avatar Aug 06 '16 01:08 hmil

Are you sure you want to bless a specific JSON api and bring in an additional dependency? What about simply providing an interface for pluggable encoders and decoders via type classes? What benefits are provided by handling json encoding/decoding in this layer vs. higher up?

easel avatar Aug 17 '16 14:08 easel

json4s is meant to be the go to way to represent JSON data in Scala. If all libraries use this, it will ease interoperability and reduce the number of different JSON AST implementations in a given application. Time will tell if this is going to happen or if this library is going to end up being "yet another json ast". The least I can do to support this effort is to allow RösHTTP to handle json4s ASTs. I don't get your point, could you elaborate?

hmil avatar Aug 21 '16 04:08 hmil

Nevermind, json4s v4 is scheduled for december so this will have to wait.

hmil avatar Sep 07 '16 03:09 hmil

Why not µPickle? This is already used in places that need JS and JVM side to work. Who knows how long you could be waiting on json4s?

It looks like json4s is still quite a ways off, looking at https://github.com/json4s/json4s/issues/256.

a1russell avatar Jan 05 '17 14:01 a1russell

Why not Circe?

antonkulaga avatar Apr 03 '17 09:04 antonkulaga

I'd like to use other third party JSON serializer too, but I agree that we don't want this library to depend on some specific JSON serializer. I think I have a solution.

I think it makes sense to let JSONBody take JSON string instead of specific type (JSONValue). Then each of us can easily create our own JSONBody using our favorite JSON serialization library.

This is current implementation:

class JSONBody private(value: JSONValue) extends BulkBodyPart {
  override def contentType: String = s"application/json; charset=utf-8"
  override def contentData: ByteBuffer = ByteBuffer.wrap(value.toString.getBytes("utf-8"))
}

object JSONBody {
  def apply(value: JSONValue): JSONBody = new JSONBody(value)
  // ...
}

It can be like below:

class JSONBody private(value: String) extends BulkBodyPart { // Note that it's taking String now
  override def contentType: String = s"application/json; charset=utf-8"
  override def contentData: ByteBuffer = ByteBuffer.wrap(value.getBytes("utf-8"))
}

object JSONBody {
  def apply(value: JSONValue): JSONBody = new JSONBody(value.toString) // We can still keep backward compatibility
  def apply(value: String): JSONBody = new JSONBody(value) // But we can now pass raw JSON
  // ...
}

class MyJSONBody[T](value: T) extends BulkBodyPart {
  private val jsonBody = JSONBody(MyFavoriteJSONSerializer.serialize(value))  // Then we all can use our favorite JSON serializer
  override def contentType: String = jsonBody.contentType
  override def contentData: ByteBuffer = jsonBody.contentData
}

Thoughts?

shogowada avatar Apr 10 '17 12:04 shogowada

I agree with @shogowada here. Perhaps it's best to just let people use their favorite libraries. The biggest tradeoff I can think of with this approach is that taking a String isn't "safe". So that apply would have to either be able to throw an exception or return an Option[JSONBody].

@antonkulaga I love Circe, too, but I think they are aiming for something more lightweight. Directly depending on Circe means depending on Cats, which is quite large.

a1russell avatar Apr 10 '17 12:04 a1russell

The JSONBody provided with RösHTTP is just a helper in case you want to create a JSON payload with RösHTTP API. I realize most of the time you are going to have another library handling the JSON in which case you'll want to use a lower-level body type or a custom body.

As it is right now, you would either define a body type like in @shogowada 's example or you would use ByteBufferBody and specify "application/json; charset=utf-8" as content-type.

A simple userland solution to avoid repetition of the content-type parameter would be to define a function like:

object MyJSONBody {
  def apply(value: string): BodyPart = ByteBufferBody(ByteBuffer.wrap(value.getBytes("utf-8")), "application/json; charset=utf-8");
}

(not 100% sure about the syntax, just writing this as I go)

What I think is really missing is a BodyPart subclass taking just a string and a content-type as arguments (and maybe an encoding...)

Could be something like:

class UTF8StringBody(value: String, override val contentType: string) { // ...

// or

class StringBody(value: String, override val contentType: string, encoding: string) { // ...

But then I wouldn't bundle the "string-to-json-body" adapter with the lib and I'd rather leave this as the users responsibility because it is a specific use case that is trivial to write on top of such a UTF8StringBody.

If someone feels like writing that (UTF8)StringBody class, PRs are welcome ;)

hmil avatar Apr 10 '17 12:04 hmil

i've actually pointed the rapture folks here for inspiration/use with their http module i guess it's come full circle as i'd point to their json module as it seems like a good reference here :). It works on both the jvm and scala.js and doesn't bless any particular json library, in line with this thread so far.

emanresusername avatar Aug 22 '17 05:08 emanresusername