JSON serialisation
The smithy4s-json
module provides jsoniter-based encoders/decoders that can read/write generated data-types from/to JSON bytes/strings, without an intermediate JSON ADT. The performance of this module is very competitive in the Scala ecosystem.
This module is provided at the following coordinates :
sbt : "com.disneystreaming.smithy4s" %% "smithy4s-json" % "0.18.33"
mill : "com.disneystreaming.smithy4s::smithy4s-json:0.18.33"
The entrypoint for JSON parsing/writing is smithy4s.json.Json
. See below for example usage.
Note: If the
Schema
is non-static, such as a dynamically generated one, then reading and writing Json may result in memory leaks.
import smithy4s.example.hello.Person
import smithy4s.Blob
import smithy4s.json.Json
val person = Person(name = "John Doe")
// person: Person = Person(name = "John Doe", town = None)
val personEncoder = Json.payloadCodecs.encoders.fromSchema(Person.schema)
// personEncoder: smithy4s.codecs.package.PayloadEncoder[Person] = smithy4s.json.internals.JsonPayloadCodecCompilerImpl$$anon$1$$anonfun$fromSchema$2@a1ef79a
val personJSON = personEncoder.encode(person).toUTF8String
// personJSON: String = "{\"name\":\"John Doe\"}"
val personJSON2 = Json.writePrettyString(person)
// personJSON2: String = """{
// "name": "John Doe"
// }"""
val personDecoder = Json.payloadCodecs.decoders.fromSchema(Person.schema)
// personDecoder: smithy4s.codecs.package.PayloadDecoder[Person] = smithy4s.json.internals.JsonPayloadCodecCompilerImpl$JsonPayloadDecoder@5a62eeb2
val maybePerson = personDecoder.decode(Blob(personJSON))
// maybePerson: Either[smithy4s.codecs.PayloadError, Person] = Right(
// value = Person(name = "John Doe", town = None)
// )
val maybePerson2 = Json.read[Person](Blob(personJSON2))
// maybePerson2: Either[smithy4s.codecs.PayloadError, Person] = Right(
// value = Person(name = "John Doe", town = None)
// )
By default, smithy4s-json
abides by the semantics of :
- official smithy traits, including:
- jsonName
- timestampFormat
- sparse
- required
- default. It is worth noting that, by default, Smithy4s chooses to not serialise default values when the member is optional.
- alloy traits
Customizing Json Codecs
Json.payloadCodecs
provides several methods for customization. A jsoniter ReaderConfig
and WriterConfig
can be provided via the
withJsoniterReaderConfig
and withJsoniterWriterConfig
methods. Additionally Smithy4s provides further customization through JsoniterCodecCompiler
.
Below is an example on how to configure the endoer to include null
for optional fields.
val personJSON3 = Json.payloadCodecs
.configureJsoniterCodecCompiler(cfg =>
cfg.withExplicitDefaultsEncoding(true)
)
.encoders
.fromSchema(Person.schema)
.encode(person)
.toUTF8String
Codec Compiler Options
The options available through JsoniterCodecCompiler
are:
withMaxArity
default: 1024
Changes the behaviour of the decoders so that they fail after a certain number of elements when decoding arrays and maps. This allows to protect against some DDOS attacks.
withFieldFilter
default: FieldFilter.Default
Changes the behaviour of Json encoders. Can be used to skip empty collections or unset optional fields to reduce the size of the final json. The default behavior skips unset optional fields or optional fields that have their value equal to the default value.
withExplicitDefaultsEncoding (deprecated)
default: false
This method is deprecated use withFieldFilter
instead.
Changes the behaviour of Json encoders so that optional values are encoded as explicit Json null values.
withFlexibleCollectionsSupport
default: false
Changes the behaviour of Json decoders so that they overlook null values in collections and maps. This behaviour has a performance overhead.
withInfinitySupport
default: false
Changes the behaviour of Json decoders so that they can parse Infinity/NaN values. This behaviour has a performance overhead.
withMapOrderPreservation
default: false
Changes the behaviour of Json decoders so that the preserve the ordering of maps.
withHintMask
Changes the hint mask with which the decoder works. Depending on the hint mask, some
smithy traits may be overlooked during encoding/decoding. For instance, @jsonName
.
withLenientTaggedUnionDecoding
Enables lenient decoding of tagged unions, where unset alternatives are encoded as null values in the json payload. Also ignores unrecognised union keys.
withLenientNumericDecoding
Enables lenient decoding of numeric values, where numbers may be carried by JSON strings as well as JSON numbers.