crate::ix!();
#[instrument(level = "trace", skip(json_value))]
pub fn deserialize_json_with_optional_fields_wrapper<T>(
json_value: &serde_json::Value,
) -> Result<T, serde_json::Error>
where
T: DeserializeOwned,
{
trace!("Attempting direct deserialization into target struct …");
match serde_json::from_value::<T>(json_value.clone()) {
Ok(t) => {
debug!("Direct deserialization succeeded.");
return Ok(t);
}
Err(e_direct) => {
debug!("Direct deserialization failed: {:?}", e_direct);
trace!("Checking for `fields` wrapper …");
if let Some(inner) = json_value.get("fields") {
trace!("`fields` wrapper found — trying inner value deserialization …");
match serde_json::from_value::<T>(inner.clone()) {
Ok(t) => {
info!("Deserialization succeeded after unwrapping `fields`.");
Ok(t)
}
Err(e_inner) => {
error!(
"Deserialization failed after unwrapping `fields`: {:?}",
e_inner
);
Err(e_inner)
}
}
} else {
error!("No `fields` wrapper present; cannot recover.");
Err(e_direct)
}
}
}
}
#[cfg(test)]
mod deserialize_json_with_optional_fields_wrapper_tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStruct {
crate_name: String,
answer: u32,
}
fn json_val(s: &str) -> serde_json::Value {
serde_json::from_str::<serde_json::Value>(s).expect("JSON must parse for test setup")
}
#[traced_test]
fn direct_deserialization_succeeds() {
trace!("===== BEGIN TEST: direct_deserialization_succeeds =====");
let input = json_val(r#"{ "crate_name": "foo", "answer": 42 }"#);
let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
assert!(
result.is_ok(),
"Direct deserialization should succeed when keys are at the top level"
);
assert_eq!(
result.unwrap(),
TestStruct {
crate_name: "foo".into(),
answer: 42
}
);
trace!("===== END TEST: direct_deserialization_succeeds =====");
}
#[traced_test]
fn fields_wrapper_deserialization_succeeds() {
trace!("===== BEGIN TEST: fields_wrapper_deserialization_succeeds =====");
let input = json_val(
r#"
{
"struct_name":"Whatever",
"fields": {
"crate_name":"bar",
"answer":1337
}
}"#,
);
let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
assert!(
result.is_ok(),
"`fields` wrapper should be detected and unwrapped automatically"
);
assert_eq!(
result.unwrap(),
TestStruct {
crate_name: "bar".into(),
answer: 1337
}
);
trace!("===== END TEST: fields_wrapper_deserialization_succeeds =====");
}
#[traced_test]
fn fails_when_neither_top_level_nor_wrapper_matches() {
trace!("===== BEGIN TEST: fails_when_neither_top_level_nor_wrapper_matches =====");
let input = json_val(r#"{ "not_it": true }"#);
let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
assert!(
result.is_err(),
"Should fail because required keys are missing and no wrapper is present"
);
trace!("===== END TEST: fails_when_neither_top_level_nor_wrapper_matches =====");
}
#[traced_test]
fn fails_when_wrapper_present_but_incorrect() {
trace!("===== BEGIN TEST: fails_when_wrapper_present_but_incorrect =====");
let input = json_val(
r#"
{
"fields": {
"wrong":"shape"
}
}"#,
);
let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
assert!(
result.is_err(),
"Should fail because inner object lacks required keys"
);
trace!("===== END TEST: fails_when_wrapper_present_but_incorrect =====");
}
#[traced_test]
fn succeeds_with_additional_unrelated_top_level_keys() {
trace!("===== BEGIN TEST: succeeds_with_additional_unrelated_top_level_keys =====");
let input = json_val(
r#"
{
"crate_name": "baz",
"answer": 7,
"extraneous": "ignored"
}"#,
);
let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
assert!(
result.is_ok(),
"Serde should ignore unrelated top‑level keys when `deny_unknown_fields` is not used"
);
assert_eq!(
result.unwrap(),
TestStruct {
crate_name: "baz".into(),
answer: 7
}
);
trace!("===== END TEST: succeeds_with_additional_unrelated_top_level_keys =====");
}
}