A Rust YAML parser used to mean serde_yaml, but that crate was archived in March 2024 and is no longer maintained. Today the choice depends on what you need. For the same serde API, use one of its forks: yaml_serde from the YAML organization, serde_yaml_ng or serde_norway. serde-saphyr is a serde layer on a pure-Rust parser, saphyr and yaml-rust2 are parsers with their own document types, and yaml-edit edits files losslessly. oneyaml puts serde and a lossless, comment-preserving Document on one YAML 1.2 parser.
Versions, dates and licenses below are from crates.io on 26 October 2026; descriptions are quoted from each crate’s own README or crates.io page.
Rust YAML crates at a glance
| Crate, latest version (date) | License | What it is, in its own words |
|---|---|---|
| serde_yaml 0.9.34+deprecated (Mar 2024) | MIT OR Apache-2.0 | “(This project is no longer maintained.)”; the repository is archived |
| yaml_serde 0.10.7 (Aug 2026) | MIT OR Apache-2.0 | “the actively maintained fork of serde-yaml, published as yaml_serde by the official YAML organization” (README) |
| serde_yaml_ng 0.10.0 (May 2024) | MIT | A fork of serde_yaml’s last commit; “This library only follows the YAML specification 1.1.” (README) |
| serde_norway 0.9.42 (Dec 2024) | MIT OR Apache-2.0 | “A hard-fork of Serde YAML.” (README) |
| serde-saphyr 2.0.0 (Oct 2026) | MIT OR Apache-2.0 | “a strongly typed YAML deserializer built on top of granit-parser”, fuzz-tested and “designed not to panic on malformed YAML” (README) |
| saphyr 0.3.2 (Oct 2026) | MIT OR Apache-2.0 | “A fully YAML 1.2 compliant YAML library” (crates.io) |
| yaml-rust2 0.13.0 (Sep 2026) | MIT OR Apache-2.0 | “A fully YAML 1.2 compliant YAML parser” (crates.io); the maintained fork of yaml-rust |
| yaml-edit 0.3.5 (Oct 2026) | Apache-2.0 | “A Rust library for parsing and editing YAML files while preserving formatting, comments, and whitespace”, on a rowan syntax tree (README) |
| oneyaml 1.2.0 (Oct 2026) | MIT OR Apache-2.0 | serde and lossless, comment-preserving editing for YAML 1.2 in one crate; no unsafe |
Skip yaml-rust: RustSec lists it as unmaintained and points to yaml-rust2 (RUSTSEC-2024-0320). The crates.io description of serde_yml, another serde_yaml successor, now begins “DEPRECATED — serde_yml is unmaintained”.
What sits under the serde_yaml forks is worth knowing. In cargo tree, serde_yaml_ng 0.10.0 depends on unsafe-libyaml and yaml_serde 0.10.7 on libyaml-rs, and each of those describes itself on crates.io as “libyaml transpiled to rust by c2rust”. oneyaml 1.2.0 depends on its own oneyaml-core and oneyaml-syntax, plus serde, and its code forbids unsafe.
Read YAML into a struct, then edit the file
oneyaml needs Rust 1.85 or newer:
cargo add [email protected] serde -F serde/deriveTyped reads use serde, the same from_str and to_string as serde_yaml. To change a file that people also edit by hand, parse it into a Document instead:
use oneyaml::Document; use serde::Deserialize; #[derive(Debug, Deserialize)] struct Config { name: String, replicas: u32, ports: Vec<u16>, } fn main() -> Result<(), Box<dyn std::error::Error>> { let src = "\ # Deploy settings name: api replicas: 2 # raised by the release job ports: [80] "; // Typed read with serde. let cfg: Config = oneyaml::from_str(src)?; println!("{cfg:?}"); // Edit the same text: only the touched values change. let mut doc = Document::parse(src)?; doc.set_path("replicas", 3)?; doc.get_mut("ports")?.push(443)?; print!("{doc}"); Ok(()) }
Config { name: "api", replicas: 2, ports: [80] } # Deploy settings name: api replicas: 3 # raised by the release job ports: [80, 443]
The comment after replicas stays, and the new port goes into the flow list in its own style. An untouched Document writes back byte for byte, and each comment stays with the entry it belongs to, by numbered rules. Both the serde read and the edit run on one parser, so they can’t disagree about what the file means.
Errors you can act on
YAML 1.2 is the default, so yes is a string, not a boolean. Read into a bool, it is an error, and how the error reads matters when a person has to fix the file. The same input through serde_yaml_ng 0.10.0 and oneyaml 1.2.0:
use serde::Deserialize; #[derive(Debug, Deserialize)] #[allow(dead_code)] struct Service { name: String, public: bool, } fn main() { let src = "name: web\npublic: yes\n"; if let Err(e) = serde_yaml_ng::from_str::<Service>(src) { println!("serde_yaml_ng: {e}\n"); } if let Err(e) = oneyaml::from_str::<Service>(src) { print!("oneyaml:\n{}", e.render(src)); } }
serde_yaml_ng: public: invalid type: string "yes", expected a boolean at line 2 column 9 oneyaml: error[Y3000]: invalid type: string "yes", expected a boolean; YAML 1.2 reads `yes` as a string (its booleans are `true` and `false`) --> 2:9 | 2 | public: yes | ^^^ | = help: write `true` instead of `yes`, or read the YAML 1.1 booleans (y/n, yes/no, on/off) as booleans with Settings::yaml11_booleans(true) or the Compat::yaml_1_1() preset = path: public = suggestion (may be incorrect): write the YAML 1.2 boolean `true`: replace `yes` with `true`
Display is one line for logs. Error::render adds a snippet with a stable Y#### code, the path, and a suggested fix you can apply in code. Files written for YAML 1.1 readers can be read as such with Compat::yaml_1_1() or Settings::yaml11_booleans(true). More in the errors recipe and the YAML 1.1 recipe.
What each crate is for
yaml_serde, serde_yaml_ng and serde_norway
The serde_yaml API, maintained. Choose one when you want to keep your code as it is and only need a crate that still gets fixes. yaml_serde’s README shows a one-line rename in Cargo.toml that keeps every use serde_yaml:: line working; serde_yaml is deprecated: what now walks through it.
serde-saphyr
A serde deserializer that reads directly into your types, on a pure-Rust parser, built to deny unsafe and not to panic on malformed input. Choose it for typed reads when you don’t need to edit files.
saphyr and yaml-rust2
Parsers with their own document and event types. Choose them when you want to walk YAML events or nodes yourself.
yaml-edit
A lossless editor that keeps formatting, comments and whitespace, with error recovery. Its README covers parsing and editing, not serde, so for typed reads of the same files you would pair it with a serde crate.
oneyaml
serde reads and writes, a Value with the serde_yaml variant names, and a lossless Document that keeps comments through edits, on one parser. A Document also applies RFC 6902 JSON Patch and RFC 7396 merge patches to the YAML text, all or nothing (JSON Patch for YAML). Every read has resource limits, with a Limits::untrusted() profile for network input (untrusted input). Its yaml-test-suite results are on the conformance page.
When another crate is the better choice
oneyaml’s own documentation lists these, and its benchmark tables name every workload where it is slower:
- Wall-clock serde read speed is what you optimize. serde_yaml_ng or serde_norway is faster in wall-clock time on some reads, such as typed reads of the OpenAPI-shaped workload. Measure on your own files.
- Many small edits to many small files. Below a few kilobytes, yaml-edit’s edits cost fewer instructions and less time.
- You need something oneyaml doesn’t do:
no_std, async I/O, schema validation, templating, or editing a file too large to hold in memory. - You need a long production track record. serde_yaml’s lineage has years of it; oneyaml’s first release was in October 2026.
How to choose a Rust YAML crate
- Existing serde_yaml code, no other changes wanted: yaml_serde, serde_yaml_ng or serde_norway.
- Typed reads only, pure Rust: serde-saphyr or oneyaml.
- You edit files people maintain, or apply patches to them: oneyaml, or yaml-edit if you don’t need serde.
- Typed reads and edits of the same files: oneyaml, which does both on one parser.
- You walk YAML events or nodes yourself: saphyr or yaml-rust2.
Start with the getting started guide, or read how oneyaml compares with the crates above, including where they win. All three oneyaml languages are on the oneyaml home page.