Working with JSON¶
Json values are supported through codecs, which encode/decode values to json strings. Most often, you’ll be using a third-party library to perform the actual json parsing/printing. Currently, Circe, µPickle, Spray JSON and Play JSON are supported.
All of the integrations, when imported into scope, define a jsonBody[T]
method. This method depends on
library-specific implicits being in scope, and derives from them a json codec. The derivation also requires implicit
Schema[T]
and Validator[T]
instances, which should be automatically derived. For more details see documentation
on supporting custom types.
If you have a custom, implicit Codec[String, T, Json]
instance, you should use the anyJsonBody[T]
method instead.
This description of endpoint input/output, instead of deriving a codec basing on other library-specific implicits, uses
the json codec that is in scope.
Circe¶
To use Circe add the following dependency to your project:
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "0.17.6"
Next, import the package (or extend the TapirJsonCirce
trait, see MyTapir):
import sttp.tapir.json.circe._
This will allow automatically deriving Codec
s which, given an in-scope circe Encoder
/Decoder
and a Schema
,
will create a codec using the json media type. Circe includes a couple of approaches to generating encoders/decoders
(manual, semi-auto and auto), so you may choose whatever suits you.
Note that when using Circe’s auto derivation, any encoders/decoders for custom types must be in scope as well.
Additionally, the above import brings into scope the jsonBody[T]
body input/output description, which uses the above
codec.
For example, to automatically generate a JSON codec for a case class:
import sttp.tapir._
import sttp.tapir.json.circe._
import sttp.tapir.generic.auto._
import io.circe.generic.auto._
case class Book(author: String, title: String, year: Int)
val bookInput: EndpointIO[Book] = jsonBody[Book]
Circe lets you select an instance of io.circe.Printer
to configure the way JSON objects are rendered. By default
Tapir uses Printer.nospaces
, which would render:
import io.circe._
Json.obj(
"key1" -> Json.fromString("present"),
"key2" -> Json.Null
)
as
{"key1":"present","key2":null}
Suppose we would instead want to omit null
-values from the object and pretty-print it. You can configure this by
overriding the jsonPrinter
in tapir.circe.json.TapirJsonCirce
:
import sttp.tapir.json.circe._
import io.circe.Printer
object MyTapirJsonCirce extends TapirJsonCirce {
override def jsonPrinter: Printer = Printer.spaces2.copy(dropNullValues = true)
}
import MyTapirJsonCirce._
Now the above JSON object will render as
{"key1":"present"}
µPickle¶
To use µPickle add the following dependency to your project:
"com.softwaremill.sttp.tapir" %% "tapir-json-upickle" % "0.17.6"
Next, import the package (or extend the TapirJsonuPickle
trait, see MyTapir and add TapirJsonuPickle
not TapirCirceJson
):
import sttp.tapir.json.upickle._
µPickle requires a ReadWriter in scope for each type you want to serialize. In order to provide one use the macroRW
macro in the companion object as follows:
import sttp.tapir._
import sttp.tapir.generic.auto._
import upickle.default._
import sttp.tapir.json.upickle._
case class Book(author: String, title: String, year: Int)
object Book {
implicit val rw: ReadWriter[Book] = macroRW
}
val bookInput: EndpointIO[Book] = jsonBody[Book]
Like Circe, µPickle allows you to control the rendered json output. Please see the Custom Configuration of the manual for details.
For more examples, including making a custom encoder/decoder, see TapirJsonuPickleTests.scala
Play JSON¶
To use Play JSON add the following dependency to your project:
"com.softwaremill.sttp.tapir" %% "tapir-json-play" % "0.17.6"
Next, import the package (or extend the TapirJsonPlay
trait, see MyTapir and add TapirJsonPlay
not TapirCirceJson
):
import sttp.tapir.json.play._
Play JSON requires Reads
and Writes
implicit values in scope for each type you want to serialize.
Spray JSON¶
To use Spray JSON add the following dependency to your project:
"com.softwaremill.sttp.tapir" %% "tapir-json-spray" % "0.17.6"
Next, import the package (or extend the TapirJsonSpray
trait, see MyTapir and add TapirJsonSpray
not TapirCirceJson
):
import sttp.tapir.json.spray._
Spray JSON requires a JsonFormat
implicit value in scope for each type you want to serialize.
Tethys JSON¶
To use Tethys JSON add the following dependency to your project:
"com.softwaremill.sttp.tapir" %% "tapir-json-tethys" % "0.17.6"
Next, import the package (or extend the TapirJsonTethys
trait, see MyTapir and add TapirJsonTethys
not TapirCirceJson
):
import sttp.tapir.json.tethysjson._
Tethys JSON requires JsonReader
and JsonWriter
implicit values in scope for each type you want to serialize.
Jsoniter Scala¶
To use Jsoniter-scala add the following dependency to your project:
"com.softwaremill.sttp.tapir" %% "tapir-jsoniter-scala" % "0.17.6"
Next, import the package (or extend the TapirJsonJsoniter
trait, see MyTapir and add TapirJsonJsoniter
not TapirCirceJson
):
import sttp.tapir.json.jsoniter._
Jsoniter Scala requires JsonValueCodec
implicit value in scope for each type you want to serialize.
Other JSON libraries¶
To add support for additional JSON libraries, see the sources for the Circe codec (which is just a couple of lines of code).
Schemas¶
To derive json codecs automatically, not only implicits from the base library are needed (e.g. a circe
Encoder
/Decoder
), but also an implicit Schema[T]
value, which provides a mapping between a type T
and its
schema. A schema-for value contains a single schema: Schema
field.
See custom types for details.
Next¶
Read on about working with forms.