spate-json
JSON deserialization for the Spate framework. Decodes JSON payloads from a source into your own serde types or dynamically-typed values.
Applications should depend on the spate facade crate with the json
feature rather than on this crate directly.
Framings
One payload maps to 0..N records, chosen by framing:
single— the whole payload is one JSON document → one record (the Kafka-message default). Empty/whitespace-only payloads are tombstones.ndjson— newline-delimited JSON (JSON Lines): one value per\n-separated line → one record per line, with per-line error isolation.array— a top-level JSON array → one record per element (decoded in one pass; a malformed array is handled atomically).
Those frame a payload already in memory (a Kafka message). For a streaming
source that delivers an object in chunks (spate-s3, an HTTP body), the
crate also owns the byte-stream framer: NdjsonFramer is a chunk-fed, bounded
RecordFramer the source runs to cut the stream into records — wire it in with
S3Source::with_framer(|| Box::new(NdjsonFramer::new(max))), then the
deserializer decodes each line in single mode.
Configuration
deserializer:
json:
framing: single # single | ndjson | array
on_error: skip # skip | fail
reject_duplicate_keys: false
let deserializer = from_component?
.with_metrics
.;
Errors and metrics
A document that does not parse (or match the target type) is a record-level
error. on_error: skip drops it, counts it in
spate_json_deser_records_dropped_total{reason}, and continues; on_error: fail
stops the batch, which replays under at-least-once. The framework's generic
spate_deser_* stage metrics wrap every decoder in addition.
Fidelity features
reject_duplicate_keys (config) turns silent last-value-wins on duplicate
object keys into an error. Opt-in Cargo features pass straight through to
serde_json: float-roundtrip, arbitrary-precision (crate-wide; interacts
with flatten/untagged/RawValue), and raw-value.
Backends
Decoding uses serde_json (stable 1.x) by default. The opt-in simd Cargo
feature (json-simd on the spate facade) swaps the byte-slice → value step to
simd-json with no change to this crate's API, and BACKEND_ID reports which
is compiled. It is off by default. simd-json is a different parser, so
decoding is not byte-for-byte identical on every input, and the serde_json
knobs above do not apply under it; the JSON connector guide's Backends section
gives the differences. from_reader is never used — decoding always operates on
the in-memory payload slice.