serde_sated

Crates.ioserde_sated
lib.rsserde_sated
version0.1.3
sourcesrc
created_at2024-04-20 21:56:16.942664
updated_at2024-05-06 06:19:39.114161
descriptionsane adjacently tagged enum deserialization (with untagged variant) for serde
homepage
repositoryhttps://github.com/muttleyxd/serde_sated
max_upload_size
id1214951
size19,890
Mateusz Szychowski (Muttley) (muttleyxd)

documentation

README

serde-sated (sane adjacently tagged enum deserialization [with untagged variant])

This crate provides a derive macro to override default serde::Deserialize behavior when deserializing adjacently tagged enum variants with fallback untagged value.

What is wrong with default Deserialize?

Take a look at following code:

use serde::{Deserialize, Serialize};
use serde_json::json;

#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "resourceType", content = "resource")]
pub enum ResourceStruct {
    Number(u64),
    String(String),
    Complex(Complex),
    #[serde(untagged)]
    Unknown(serde_json::Value),
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Complex {
    pub a: u64,
    pub b: u64,
}

#[test]
fn deserialize_json() {
    let missing_field_b_in_complex_variant = json!({
        "resourceType": "Complex",
        "resource": {
            "a": 2000
        }
    });

    let result = serde_json::from_value::<ResourceStruct>(missing_field_b_in_complex_variant).unwrap();
    eprintln!("Resource: {:#?}", result);
}

This will print:

Resource: Unknown(
    Object {
        "resource": Object {
            "a": Number(2000),
        },
        "resourceType": String("Complex"),
    },
)

As you can see, missing field "b" in Complex variant caused serde to default to untagged variant.

Solution

This may or may not be the desired behavior - this crate allows to change that, instead of defaulting to untagged variant it will return the correct error.

Let's change the ResourceStruct derive attribute to use serde_sated::deserialize_enum_with_untagged_as_fallback

use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_sated::deserialize_enum_with_untagged_as_fallback;

#[derive(Debug, deserialize_enum_with_untagged_as_fallback, Serialize)]
#[serde(tag = "resourceType", content = "resource")]
pub enum ResourceStruct {
    Number(u64),
    String(String),
    Complex(Complex),
    #[serde(untagged)]
    Unknown(serde_json::Value),
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Complex {
    pub a: u64,
    pub b: u64,
}

#[test]
fn deserialize_json() {
    let missing_field_b_in_complex_variant = json!({
        "resourceType": "Complex",
        "resource": {
            "a": 2000
        }
    });

    let result = serde_json::from_value::<ResourceStruct>(missing_field_b_in_complex_variant);
    eprintln!("Resource: {:#?}", result);
}

Now the result will be:

Resource: Err(
    Error("missing field `b`", line: 0, column: 0),
)
Commit count: 8

cargo fmt