serde_ignored

Find out which JSON, YAML, or TOML keys get silently dropped during Serde deserialization

Library
Cargo
v0.1.14
105stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
44/100Fair
Development Activity36
Maintenance32
Community36
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture72
Code Quality76
Innovation80
Learning Curve68

serde_ignored wraps any existing Serde Deserializer and invokes a callback with the dotted path of every field that gets ignored while deserializing into a struct. Rather than silently discarding unrecognized keys, you get a stream of paths like dependencies.serde.typo1 or typo2, which is exactly the kind of feedback config file parsers and CLI tools want to surface to users as a warning.

The crate works by intercepting deserialize_ignored_any, the hook Serde already calls internally whenever a map or struct field has no matching target field, so it requires no changes to your existing Deserialize derives. It ships #![no_std] (with alloc) and has a single production dependency, making it cheap to drop into config-loading code, most famously inside Cargo itself for catching typos in Cargo.toml.

What You Get

  • A single serde_ignored::deserialize(deserializer, callback) entry point that wraps any existing Serde Deserializer transparently
  • A Path enum that renders the exact location of an ignored field as a dotted string, e.g. dependencies.serde.typo1
  • Zero changes required to your existing Deserialize derives or struct definitions
  • #![no_std] support (with alloc) for use in constrained environments
  • Works with any format that has a Serde Deserializer implementation — JSON, YAML, TOML, etc., not just serde_json

Common Use Cases

  • Warning users about typo’d or deprecated keys in a config file (TOML, YAML, JSON) instead of silently ignoring them
  • Auditing Cargo.toml-style manifest files for unrecognized fields, the motivating use case inside Cargo itself
  • Surfacing schema drift when consuming third-party or user-supplied JSON payloads during a migration
  • Building lint/validation tooling on top of existing deserialization code without duplicating the schema

Under The Hood

Architecture The crate is a single-file (src/lib.rs) implementation built around one core struct, Deserializer<'a, 'b, D, F>, which wraps an arbitrary inner D: de::Deserializer<'de> plus a &mut F: FnMut(Path) callback and a borrowed Path<'a> representing the current traversal location. Rather than re-implementing deserialization, it implements Serde’s full Deserializer trait and forwards every method (deserialize_bool, deserialize_u8, deserialize_map, deserialize_seq, …) straight through to the inner deserializer, wrapping the visitor in a Wrap type that carries the callback and current path down through nested SeqAccess/MapAccess calls. The one method that actually does work is deserialize_ignored_any, which Serde’s derive macro already calls internally whenever it encounters a map key with no matching struct field — that single interception point is where the callback fires with the current Path. Path is deliberately a borrowed, stack-allocated linked list (Root, Seq { parent, index }, Map { parent, key }, Some { parent }, NewtypeStruct, NewtypeVariant) rather than an owned Vec<String>, so traversing arbitrarily deep structures costs no heap allocation until Display::fmt walks the parent chain and formats the final dotted path string on demand. Tech Stack The crate declares #![no_std] and depends only on alloc plus serde_core (re-exported internally as serde) pinned to >=1.0.220, which is the trait-only split of Serde introduced for faster compile times and no-std compatibility; a [target.'cfg(any())'.dependencies] block additionally pulls in the full serde crate purely to keep its version in the lockfile without actually compiling it in. Dev-dependencies (serde, serde_derive, serde_json) are used only for tests and the doc example. Edition 2021, MSRV pinned at 1.71 and enforced in CI. Code Quality tests/test.rs centers on a single assert_ignored helper that deserializes JSON via serde_json::Deserializer, collects reported paths into a BTreeSet, and asserts them against an expected set; the four tests cover the README’s nested-struct example plus sequences, options, and enum variants, exercising most of the Path enum’s branches. There is no unsafe code anywhere in the crate. CI (dtolnay/serde-ignored/.github/workflows/ci.yml) runs the test suite across nightly/beta/stable/MSRV Rust with -Dwarnings, adds a minimal-versions dependency check, and lints the rustdoc build separately — a notably thorough matrix for a ~40KB crate. API Design The public surface is intentionally minimal: one function, serde_ignored::deserialize(deserializer, callback), that a caller drops around an existing Deserializer with zero changes to their Deserialize derives — the doc comment on lib.rs doubles as a runnable doctest matching the README example verbatim, so there’s effectively no gap between reading the docs and using the crate correctly.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search