diff --git a/argonaut/src/main/scala/io/finch/argonaut/Decoders.scala b/argonaut/src/main/scala/io/finch/argonaut/Decoders.scala index 511f039e8..4e23d7194 100644 --- a/argonaut/src/main/scala/io/finch/argonaut/Decoders.scala +++ b/argonaut/src/main/scala/io/finch/argonaut/Decoders.scala @@ -1,6 +1,7 @@ package io.finch.argonaut import argonaut.{CursorHistory, DecodeJson, Json} +import com.twitter.io.{Buf, Charsets} import com.twitter.util.{Return, Throw, Try} import io.finch._ import io.finch.internal.BufText @@ -16,12 +17,11 @@ trait Decoders { implicit def decodeArgonaut[A](implicit d: DecodeJson[A]): Decode.Json[A] = Decode.json { (b, cs) => val err: (String, CursorHistory) => Try[A] = { (str, hist) => Throw[A](Error(str)) } - - // TODO: Eliminate toString conversion - // See https://github.com/finagle/finch/issues/511 - // Jawn can parse from ByteBuffer's if they represent UTF-8 strings. - // We can check the charset here and do parsing w/o extra to-string conversion. - Parser.parseFromString[Json](BufText.extract(b, cs))(facade) match { + val attemptJson = cs match { + case Charsets.Utf8 => Parser.parseFromByteBuffer[Json](Buf.ByteBuffer.Shared.extract(b))(facade) + case _ => Parser.parseFromString[Json](BufText.extract(b, cs))(facade) + } + attemptJson match { case Success(value) => d.decodeJson(value).fold[Try[A]](err, Return(_)) case Failure(error) => Throw[A](Error(error.getMessage)) } diff --git a/circe/src/main/scala/io/finch/circe/Decoders.scala b/circe/src/main/scala/io/finch/circe/Decoders.scala index d720f92de..d4c744e5a 100644 --- a/circe/src/main/scala/io/finch/circe/Decoders.scala +++ b/circe/src/main/scala/io/finch/circe/Decoders.scala @@ -1,9 +1,10 @@ package io.finch.circe import cats.syntax.show._ +import com.twitter.io.{Buf, Charsets} import com.twitter.util.{Return, Throw, Try} import io.circe.Decoder -import io.circe.jawn.decode +import io.circe.jawn._ import io.finch.{Decode, Error} import io.finch.internal.BufText @@ -12,15 +13,11 @@ trait Decoders { /** * Maps a Circe's [[Decoder]] to Finch's [[Decode]]. */ - implicit def decodeCirce[A: Decoder]: Decode.Json[A] = Decode.json((b, cs) => - - // TODO: Eliminate toString conversion - // See https://github.com/finagle/finch/issues/511 - // Jawn can parse from ByteBuffer's if they represent UTF-8 strings. - // We can check the charset here and do parsing w/o extra to-string conversion. - decode[A](BufText.extract(b, cs)).fold[Try[A]]( - error => Throw[A](Error(error.show)), - value => Return(value) - ) - ) + implicit def decodeCirce[A: Decoder]: Decode.Json[A] = Decode.json({ (b, cs) => + val attemptJson = cs match { + case Charsets.Utf8 => parseByteBuffer(Buf.ByteBuffer.Shared.extract(b)).right.flatMap(_.as[A]) + case _ => decode[A](BufText.extract(b, cs)) + } + attemptJson.fold[Try[A]](error => Throw[A](Error(error.show)), value => Return(value)) + }) } diff --git a/jackson/src/main/scala/io/finch/jackson/package.scala b/jackson/src/main/scala/io/finch/jackson/package.scala index bfb8b97a9..059f84a7e 100644 --- a/jackson/src/main/scala/io/finch/jackson/package.scala +++ b/jackson/src/main/scala/io/finch/jackson/package.scala @@ -1,20 +1,17 @@ package io.finch -import scala.reflect.ClassTag - import com.fasterxml.jackson.databind.ObjectMapper +import com.twitter.io.Buf import com.twitter.util.Try import io.finch.internal.BufText +import scala.reflect.ClassTag package object jackson { implicit def decodeJackson[A](implicit mapper: ObjectMapper, ct: ClassTag[A] ): Decode.Json[A] = Decode.json((b, cs) => - // TODO: Eliminate toString conversion - // See https://github.com/finagle/finch/issues/511 - // Jackson can parse from byte[] automatically detecting the encoding. - Try(mapper.readValue(BufText.extract(b, cs), ct.runtimeClass.asInstanceOf[Class[A]])) + Try(mapper.readValue(Buf.ByteArray.Shared.extract(b), ct.runtimeClass.asInstanceOf[Class[A]])) ) implicit def encodeJackson[A](implicit mapper: ObjectMapper): Encode.Json[A] = diff --git a/playjson/src/main/scala/io/finch/playjson/package.scala b/playjson/src/main/scala/io/finch/playjson/package.scala index 7ad906548..732d0d9ec 100644 --- a/playjson/src/main/scala/io/finch/playjson/package.scala +++ b/playjson/src/main/scala/io/finch/playjson/package.scala @@ -1,5 +1,6 @@ package io.finch +import com.twitter.io.Buf import com.twitter.util.Try import io.finch.internal.BufText import play.api.libs.json._ @@ -11,10 +12,7 @@ package object playjson { * @tparam A the type of the data to decode into */ implicit def decodePlayJson[A](implicit reads: Reads[A]): Decode.Json[A] = - // TODO: Eliminate toString conversion - // See https://github.com/finagle/finch/issues/511 - // PlayJson can parse from byte[] automatically detecting the charset. - Decode.json((b, cs) => Try(Json.parse(BufText.extract(b, cs)).as[A])) + Decode.json((b, cs) => Try(Json.parse(Buf.ByteArray.Shared.extract(b)).as[A])) /** * @param writes Play JSON `Writes` to use for encoding diff --git a/sprayjson/src/main/scala/io/finch/sprayjson/package.scala b/sprayjson/src/main/scala/io/finch/sprayjson/package.scala index de11a88a3..57db6efd0 100644 --- a/sprayjson/src/main/scala/io/finch/sprayjson/package.scala +++ b/sprayjson/src/main/scala/io/finch/sprayjson/package.scala @@ -1,5 +1,6 @@ package io.finch +import com.twitter.io.{Buf, Charsets} import com.twitter.util.Try import io.finch.internal.BufText import spray.json._ @@ -11,10 +12,12 @@ package object sprayjson{ * @tparam A the type of the data to decode into */ implicit def decodeSpray[A](implicit format: JsonFormat[A]): Decode.Json[A] = - // TODO: Eliminate toString conversion - // See https://github.com/finagle/finch/issues/511 - // SprayJson can parse from byte[] if it represents a UTF-8 string. - Decode.json((b, cs) => Try(BufText.extract(b, cs).parseJson.convertTo[A])) + Decode.json({ (b, cs) => + cs match { + case Charsets.Utf8 => Try(JsonParser(Buf.ByteArray.Shared.extract(b)).convertTo[A]) + case _ => Try(BufText.extract(b, cs).parseJson.convertTo[A]) + } + }) /** * @param format spray-json support for convert JSON val to specific type object