ytsaurus-yson
A YSON serializer and deserializer (text and binary) built on serde, for talking to YTsaurus.
This crate is a fork of ss123she/yson-rs, vendored at revision
ba2044c711cefa65259e25122fea21c36f451093(2026-04-01, published upstream as v0.1.3). Nearly all of the code is upstream's work. Everything this fork changed is listed in CHANGELOG.md.The fork exists so that the job runtime can evolve the parser (streaming reads, in particular) without waiting on upstream. It is not published to crates.io, and the
yson-rsname is not ours to claim.
Licensed under Apache-2.0.
Upstream offers yson-rs under either MIT or Apache-2.0; this fork takes it under Apache-2.0, which that dual offer permits. The upstream licence texts are retained here as received — see NOTICE for the full picture.
Usage
use ;
use ;
let row = Row ;
let bytes = to_vec?;
let decoded: Row = from_slice?;
assert_eq!;
# Ok::
Attributes map to @-prefixed fields, and $value holds the body of an
attributed scalar:
use ;
// YSON: <table_index=1>#
For columns holding arbitrary bytes rather than text, use
serde_bytes — see the note below.
Binary format reference
The markers this crate implements, from the YSON spec:
| Marker | Type | Payload |
|---|---|---|
0x01 |
string | zigzag varint length (sint32), then that many raw bytes |
0x02 |
int64 | zigzag varint (sint64) |
0x03 |
double | 8 bytes, little-endian |
0x04 / 0x05 |
boolean | false / true |
0x06 |
uint64 | unsigned varint |
0x23 # |
entity | — |
0x3C 0x3E < > |
attributes | |
0x5B 0x5D [ ] |
list | |
0x7B 0x7D { } |
map | |
0x3D = |
key/value separator | |
0x3B ; |
item separator |
Testing
Beyond upstream's suite, this fork adds golden-byte tests for every YTsaurus control record, round trips against fixtures generated by the Go YSON implementation, and a deterministic no-panic sweep. Performance baseline: BENCHMARKS.md.
The libFuzzer targets in fuzz/ need nightly and a separate install:
tests/fuzz_smoke_tests.rs covers the same entry points deterministically so that
CI still gets a no-panic signal without them.
Known limitations
Things worth knowing before relying on this crate. None of them are known to be wrong for the YTsaurus job protocol, but several would bite outside it.
-
The API takes a whole slice.
from_sliceneeds the entire document in memory. Streaming a job's input — which can be far larger than RAM — is the job ofytsaurus-job, which reparses at record boundaries. -
Maps do not preserve key order.
YsonNode::Mapis aBTreeMap, so decoding and re-encoding sorts the keys. Values round-trip exactly; bytes do not. YTsaurus does not attach meaning to column order, so this is safe for rows, but it means you cannot use this type for byte-exact pass-through — use the raw-bytes path inytsaurus-jobfor that. -
Stringis preferred over bytes when decoding. A YSON string that happens to be valid UTF-8 is offered to serde as astr; only invalid UTF-8 is offered as bytes. Decoding intoStringtherefore fails on non-UTF-8 columns. For byte columns declare the field as#[serde(with = "serde_bytes")] Vec<u8>, which accepts both.YsonValuealways keeps the raw bytes. -
Text-mode doubles lose precision. Serialising to text goes through a shortest-representation formatter, and YTsaurus documents this as lossy in general. Use binary for anything numeric you care about — which is what jobs use anyway.
-
Recursion is capped at depth 128, and the limit is not configurable. Deeper documents fail with
Recursion limit exceededrather than overflowing the stack. Table rows never come close. -
YsonValue'sIndeximpl panics on a missing key or a non-map value. Preferattr()/as_str()/as_i64(), which returnOption. -
Attribute keys collide with
@-prefixed field names. Attributes are surfaced as fields named@name, so a map that genuinely contains the key@nameis indistinguishable from an attribute calledname. YTsaurus does not produce such column names. -
No
i128/u128support. YSON has no such type; values outsideint64/uint64are rejected at serialisation time.