samscott89 / serde_qs

Serde support for querystring-style strings
Apache License 2.0
193 stars 68 forks source link

Nested struct issue #87

Closed fcoury closed 1 year ago

fcoury commented 1 year ago

I have a nested struct that is failing to be deserialized back to the original struct, giving me this error:

running 1 test
thread 'complex_struct_and_enums' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `field`")', tests/test_regression.rs:71:57
stack backtrace:
   0: rust_begin_unwind
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/std/src/panicking.rs:593:5
   1: core::panicking::panic_fmt
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/panicking.rs:67:14
   2: core::result::unwrap_failed
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/result.rs:1651:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/result.rs:1076:23
   4: test_regression::complex_struct_and_enums
             at ./tests/test_regression.rs:71:19
   5: test_regression::complex_struct_and_enums::{{closure}}
             at ./tests/test_regression.rs:24:31
   6: core::ops::function::FnOnce::call_once
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/ops/function.rs:250:5
   7: core::ops::function::FnOnce::call_once
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
test complex_struct_and_enums ... FAILED

Here's a test case that replicates the issue:

#[test]
fn complex_struct_and_enums() {
    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub struct Query {
        pub filter: Option<Clause>,
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub struct Clause {
        pub operator: String,
        pub items: Vec<ClauseItem>,
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub enum ClauseItem {
        Clause(Clause),
        Filter(Filter),
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub struct Filter {
        pub field: String,
        pub operator: String,
        pub value: String,
    }

    let query = Query {
        filter: Some(Clause {
            operator: "And".to_string(),
            items: vec![ClauseItem::Clause(Clause {
                operator: "Or".to_string(),
                items: vec![
                    ClauseItem::Filter(Filter {
                        field: "Patient.FirstName".to_string(),
                        operator: "Contains".to_string(),
                        value: "john".to_string(),
                    }),
                    ClauseItem::Filter(Filter {
                        field: "Patient.LastName".to_string(),
                        operator: "Contains".to_string(),
                        value: "john".to_string(),
                    }),
                ],
            })],
        }),
    };

    let encoded = serde_qs::to_string(&query).unwrap();
    let decoded = serde_qs::from_str::<Query>(&encoded).unwrap();
    assert_eq!(decoded, query);
}

Is this a bug or expected behavior?

Thanks!

fcoury commented 1 year ago

Found my way through the source code and figured it out - it's the max_depth setting. This works as expected:

#[test]
fn complex_struct_and_enums() {
    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub struct Query {
        pub filter: Clause,
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub struct Clause {
        pub operator: String,
        pub items: Vec<ClauseItem>,
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub enum ClauseItem {
        Clause(Clause),
        Filter(Filter),
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    pub struct Filter {
        pub field: String,
        pub operator: String,
        pub value: String,
    }

    let query = Query {
        filter: Clause {
            operator: "And".to_string(),
            items: vec![ClauseItem::Clause(Clause {
                operator: "Or".to_string(),
                items: vec![
                    ClauseItem::Filter(Filter {
                        field: "Patient.FirstName".to_string(),
                        operator: "Contains".to_string(),
                        value: "john".to_string(),
                    }),
                    ClauseItem::Filter(Filter {
                        field: "Patient.LastName".to_string(),
                        operator: "Contains".to_string(),
                        value: "john".to_string(),
                    }),
                ],
            })],
        },
    };

    let encoded = serde_qs::to_string(&query).unwrap();
    let config = Config::new(10, true);
    let decoded: Query = config.deserialize_str(&encoded).unwrap();
    assert_eq!(decoded, query);
}
running 1 test
test complex_struct_and_enums ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s