#serde-yaml #fields #serde #ignored #field

serde-ignored-fields

preserve ignored fields with serde

2 releases

0.1.1 Aug 5, 2024
0.1.0 Jul 22, 2024

#976 in Parser implementations

45 downloads per month

BSD-2-Clause

50KB
1K SLoC

serde-ignored-fields

De-serialize and re-serialize a type while preserving ignored fields.

Sometimes you may wish to preserve the ignored fields of something you are deserializing. If you are in controll of the type, you could make use of the #[serde(flatten)] attribute:

#
#[derive(serde::Deserialize, serde::Serialize)]
struct Thing {
  name: String,

  #[serde(flatten)]
  extra_fields: serde_yaml::Mapping,
}
#
#

This crate can help you if you are not in control of the type. You can wrap the type in PreserveIgnoredFields:

use serde_ignored_fields::PreserveIgnoredFields;

#[derive(serde::Deserialize, serde::Serialize)]
struct Thing {
  name: String,
}

let thing: PreserveIgnoredFields<Thing, serde_yaml::Mapping> = serde_yaml::from_str("
  name: Turbo Encabulator
  base_plate:
    prefabulated: true
    material: aluminite
  casing: malleable logarithmic
")?;

assert!(thing.value.name == "Turbo Encabulator");
assert!(thing.ignored_fields["base_plate"]["prefabulated"] == true);
assert!(thing.ignored_fields["base_plate"]["material"] == "aluminite");
assert!(thing.ignored_fields["casing"] == "malleable logarithmic");

If you enable the schemars feature, [PreserveIgnoredFields<T, U>] implements the schemars::JsonSchema trait. It forwards directly to the schemars::JsonSchema implementation of T.

Limitations

Because serde does not provide first class support for capturing ignored fields, there are some limitations.

Self-describing format

First, PreserveIgnoredFields only works with a self-describing format such as JSON, YAML or TOML. This should not come as a surprise, and will not be a real limitation in practise (how can you have ignored fields if the data format doesn't tell you what the fields are?).

In serde terms: the serde::Deserializer must support serde::Deserializer::deserialize_any().

Serialize/Deserialize implementations

Secondly, the type T being (de)serialized must be represented as a key/value map, and it must call serde::Deserializer::deserialize_ignored_any() to deserialize ignored fields. It must not produce an error when encountering an unknown field (so the type must not use #[serde(deny_unknown_fields)]).

In particular, this means that it will not work for externally tagged enums, internally tagged enums and untagged enums. Externally tagged enums are not always serialized as key/value maps (the serialization format controls their layout). Internally and untagged enums have to look at fields before knowing which of the fields are actually going to be ignored. This crate does work with adjectently tagged enums.

It also means that it will not work for types that first deserialize into something like serde_json::Value before processing the value further. When deserialized, the serde_json::Value uses all fields. The next processing step may discard them again, but there is no way for PreserveIgnoredFields to know about this.

In summary: Using PreserveIgnoredFields with structs that use the standard serde derive macros from serde will work, as long as you did not use #[serde(deny_unknown_fields)]. Using it with enums that use the standard derive macros will only work if they are adjectently tagged (they have a serde tag = "..." and content = "..." attribute).

Dependencies

~0.1–9MB
~100K SLoC