Serialization¶
JSONSerializer¶
JSONSerializer
¶
Default JSON serializer.
Supports: - dict/list → json.dumps/loads - Pydantic V2 models → model_validate_json/model_dump_json - dataclasses → asdict → json.dumps - str → encode to UTF-8 - bytes → pass through
By default the serializer raises on objects json cannot represent
(e.g. datetime, Decimal) instead of silently coercing them via
str(). Pass coerce_unknown_to_str=True to restore the legacy
default=str coercion behaviour.
H14 — decoding into a stdlib dataclass does no type validation or
coercion: a field declared qty: int silently receives whatever
JSON type was actually present if the producer sent the wrong type.
Unknown keys in the payload are dropped rather than raising. Use a
Pydantic model as target_type (or a msgspec-based serializer)
instead of a stdlib dataclass for untrusted input where wrong-typed
fields must be rejected.
Source code in src/rabbitkit/serialization/json.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
Methods:¶
encode(data: Any) -> bytes
¶
Serialize data to JSON bytes.
Source code in src/rabbitkit/serialization/json.py
decode(data: bytes, target_type: type) -> Any
¶
Deserialize JSON bytes to target type.
Source code in src/rabbitkit/serialization/json.py
MsgspecSerializer¶
MsgspecSerializer
¶
High-performance serializer using msgspec.
Requires msgspec to be installed (optional dependency). Falls back with clear error if not available.
M7: caps the input size before decoding (64 MiB by default, matching
CompressionMiddleware's max_decompressed_size default) — without
it, a large uncompressed body is fully materialized with no bound.
Pass max_parse_bytes=None to opt out.
Source code in src/rabbitkit/serialization/msgspec.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
Attributes¶
content_type: str
property
¶
Advisory only (M10): this is the content_type used when
publishing (set on the outgoing AMQP message property). It is
not verified against an incoming message's declared
content_type on :meth:decode — decode is driven solely by the
handler's declared parameter type, matching every other built-in
serializer in this codebase (none of them negotiate/verify
content-type on the consume side). If a message's actual body
doesn't match what this serializer expects (e.g. it's not JSON at
all), :meth:decode raises with a message naming both the target
type and a content-type-mismatch hint, rather than a raw
msgspec.DecodeError.
Methods:¶
encode(data: Any) -> bytes
¶
Serialize data to JSON bytes using msgspec.
Source code in src/rabbitkit/serialization/msgspec.py
decode(data: bytes, target_type: type) -> Any
¶
Deserialize JSON bytes using msgspec.
M10: raises with a clearer message (naming the target type and
hinting at a content-type mismatch) instead of a raw
msgspec.DecodeError if the body isn't valid JSON for
target_type — most commonly caused by a message whose actual
content_type doesn't match what this serializer expects (see
:attr:content_type's docstring — this is never verified upfront).
Source code in src/rabbitkit/serialization/msgspec.py
SerializationPipeline¶
SerializationPipeline
¶
Two-stage serialization. Implements Serializer protocol.
Source code in src/rabbitkit/serialization/pipeline.py
Parsers & Decoders¶
JsonParser
¶
Built-in JSON parser.
By default serialize raises on objects json cannot represent
(e.g. datetime, Decimal) rather than silently coercing them via
str(). Pass coerce_unknown_to_str=True to restore the legacy
default=str coercion behaviour.
M7: caps the input size before json.loads (64 MiB by default,
matching CompressionMiddleware's max_decompressed_size default)
-- without it, a large uncompressed body is fully materialized with no
bound. Pass max_parse_bytes=None to opt out.
Source code in src/rabbitkit/serialization/pipeline.py
PydanticDecoder
¶
Decoder that uses Pydantic model_validate for decoding.
Companion to DataclassDecoder's H14 note: this is the decoder that
note recommends for untrusted input, on the premise that
model_validate rejects wrong-shaped data instead of silently
accepting it. That premise used to be undermined by an
isinstance(data, dict) guard here -- a non-dict top-level payload
(a producer sending a JSON array/string/number instead of an object,
whether by bug or by an attacker probing for a decoder that skips
validation on the untaken branch) bypassed model_validate entirely
and returned the raw, un-validated value straight to the handler.
Pydantic already raises a clean ValidationError for a non-dict
input (model_type error), so there is no reason to special-case
it -- doing so only suppressed the exact validation this decoder
exists to provide.
Source code in src/rabbitkit/serialization/pipeline.py
DataclassDecoder
¶
Decoder for stdlib dataclasses.
H14 — no type validation or coercion: unlike PydanticDecoder, this
does NOT check field types. A field declared qty: int silently
receives whatever JSON type was actually present (e.g. the string
"3") if the producer sent the wrong type — stdlib dataclasses
perform no runtime type checking on construction. Use
PydanticDecoder (or a msgspec-based decoder) instead for untrusted
input where wrong-typed fields must be rejected.
Unknown keys in the incoming dict (not a declared field) are silently
dropped rather than raising TypeError — this keeps a producer that
adds a new field (forward-compatible) from turning every message into a
decode failure (misclassified PERMANENT, straight to the DLQ) until
every consumer is upgraded. A genuinely wrong shape (a missing required
field, etc.) still raises — as a TypeError naming the target
dataclass, not a bare constructor traceback.
Source code in src/rabbitkit/serialization/pipeline.py
RawDecoder
¶
Pass-through decoder — no transformation.
Source code in src/rabbitkit/serialization/pipeline.py
MessageParser
¶
Bases: Protocol
Stage 1: raw bytes → intermediate form.
Source code in src/rabbitkit/serialization/pipeline.py
MessageDecoder
¶
Bases: Protocol
Stage 2: intermediate → typed Python object.