aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt13
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/package.scala1
-rw-r--r--docs/json.rst27
-rw-r--r--json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala (renamed from circe/src/main/scala/com/softwaremill/sttp/circe/package.scala)1
-rw-r--r--json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala (renamed from circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala)0
-rw-r--r--json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala14
-rw-r--r--json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala75
7 files changed, 128 insertions, 3 deletions
diff --git a/build.sbt b/build.sbt
index ec634f9..651ddf3 100644
--- a/build.sbt
+++ b/build.sbt
@@ -58,6 +58,7 @@ lazy val rootProject = (project in file("."))
okhttpBackend,
okhttpMonixBackend,
circe,
+ json4s,
tests
)
@@ -151,7 +152,7 @@ lazy val okhttpMonixBackend: Project = (project in file("okhttp-backend/monix"))
libraryDependencies ++= Seq(monix)
) dependsOn okhttpBackend
-lazy val circe: Project = (project in file("circe"))
+lazy val circe: Project = (project in file("json/circe"))
.settings(commonSettings: _*)
.settings(
name := "circe",
@@ -162,6 +163,16 @@ lazy val circe: Project = (project in file("circe"))
)
) dependsOn core
+lazy val json4s: Project = (project in file("json/json4s"))
+ .settings(commonSettings: _*)
+ .settings(
+ name := "json4s",
+ libraryDependencies ++= Seq(
+ "org.json4s" %% "json4s-native" % "3.5.3",
+ scalaTest % "test"
+ )
+ ) dependsOn core
+
lazy val tests: Project = (project in file("tests"))
.settings(commonSettings: _*)
.settings(
diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala
index c1178d9..ed685fa 100644
--- a/core/src/main/scala/com/softwaremill/sttp/package.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/package.scala
@@ -51,6 +51,7 @@ package object sttp {
"application/x-www-form-urlencoded"
private[sttp] val TextPlainContentType = "text/plain"
private[sttp] val MultipartFormDataContentType = "multipart/form-data"
+ private[sttp] val ApplicationJsonContentType = "application/json"
// entry points
diff --git a/docs/json.rst b/docs/json.rst
index 032c120..0d3a8ce 100644
--- a/docs/json.rst
+++ b/docs/json.rst
@@ -30,4 +30,29 @@ This module adds a method to the request and a function that can be given to a r
.response(asJson[Response])
.send()
-
+Json4s
+------
+
+To encode and decode json using json4s-native, add the following dependency to your project::
+
+ "com.softwaremill.sttp" %% "json4s" % "0.0.20"
+
+Using this module it is possible to set request bodies and read response bodies as case classes, using the implicitly available ``org.json4s.Formats`` (which defaults to ``org.json4s.DefaultFormats``).
+
+Usage example::
+
+ import com.softwaremill.sttp._
+ import com.softwaremill.sttp.json4s._
+
+ implicit val backend = HttpURLConnectionBackend()
+
+ case class Payload(...)
+ val requestPayload: Payload = Payload(...)
+
+ val response: Response[Payload] =
+ sttp
+ .post(uri"...")
+ .body(requestPayload)
+ .response(asJson[Response])
+ .send()
+
diff --git a/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala b/json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala
index 77a926d..ddc9583 100644
--- a/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala
+++ b/json/circe/src/main/scala/com/softwaremill/sttp/circe/package.scala
@@ -4,7 +4,6 @@ import io.circe.parser._
import io.circe.{Decoder, Encoder}
package object circe {
- private[sttp] val ApplicationJsonContentType = "application/json"
implicit def circeBodySerializer[B](
implicit encoder: Encoder[B]): BodySerializer[B] =
diff --git a/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala b/json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
index 1e7dddc..1e7dddc 100644
--- a/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
+++ b/json/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
diff --git a/json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala b/json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala
new file mode 100644
index 0000000..4c7aa36
--- /dev/null
+++ b/json/json4s/src/main/scala/com/softwaremill/sttp/json4s/package.scala
@@ -0,0 +1,14 @@
+package com.softwaremill.sttp
+
+import org.json4s._
+import org.json4s.native.Serialization.{read, write}
+
+package object json4s {
+ implicit def json4sBodySerializer[B <: AnyRef](
+ implicit formats: Formats = DefaultFormats): BodySerializer[B] =
+ b => StringBody(write(b), Utf8, Some(ApplicationJsonContentType))
+
+ def asJson[B: Manifest](
+ implicit formats: Formats = DefaultFormats): ResponseAs[B, Nothing] =
+ asString(Utf8).map(s => read[B](s))
+}
diff --git a/json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala b/json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala
new file mode 100644
index 0000000..bb4a774
--- /dev/null
+++ b/json/json4s/src/test/scala/com/softwaremill/sttp/Json4sTests.scala
@@ -0,0 +1,75 @@
+package com.softwaremill.sttp
+
+import org.json4s.ParserUtil.ParseException
+import org.scalatest._
+
+import scala.language.higherKinds
+
+class Json4sTests extends FlatSpec with Matchers with EitherValues {
+ import json4s._
+ import Json4sTests._
+
+ "The json4s module" should "encode arbitrary json bodies" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val expected = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+
+ val req = sttp.body(body)
+
+ extractBody(req) shouldBe expected
+ }
+
+ it should "decode arbitrary bodies" in {
+ val body = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+ val expected = Outer(Inner(42, true, "horses"), "cats")
+
+ val responseAs = asJson[Outer]
+
+ runJsonResponseAs(responseAs)(body) shouldBe expected
+ }
+
+ it should "fail to decode invalid json" in {
+ val body = """not valid json"""
+
+ val responseAs = asJson[Outer]
+
+ an[ParseException] should be thrownBy runJsonResponseAs(responseAs)(body)
+ }
+
+ it should "set the content type" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val req = sttp.body(body)
+
+ val ct = req.headers.toMap.get("Content-Type")
+
+ ct shouldBe Some(contentTypeWithEncoding(ApplicationJsonContentType, Utf8))
+ }
+
+ def extractBody[A[_], B, C](request: RequestT[A, B, C]): String =
+ request.body match {
+ case StringBody(body, "utf-8", Some(ApplicationJsonContentType)) =>
+ body
+ case wrongBody =>
+ fail(
+ s"Request body does not serialize to correct StringBody: $wrongBody")
+ }
+
+ def runJsonResponseAs[A](responseAs: ResponseAs[A, Nothing]): String => A =
+ responseAs match {
+ case responseAs: MappedResponseAs[_, A, Nothing] =>
+ responseAs.raw match {
+ case ResponseAsString("utf-8") =>
+ responseAs.g
+ case ResponseAsString(encoding) =>
+ fail(
+ s"MappedResponseAs wraps a ResponseAsString with wrong encoding: $encoding")
+ case _ =>
+ fail("MappedResponseAs does not wrap a ResponseAsString")
+ }
+ case _ => fail("ResponseAs is not a MappedResponseAs")
+ }
+}
+
+object Json4sTests {
+ case class Inner(a: Int, b: Boolean, c: String)
+ case class Outer(foo: Inner, bar: String)
+}