Reducing structs in Serde deserialization

I have some JSON data I want to deserialize that looks similar to

{
  "a" : {
    "b": {
      ACTUAL DATA
    }
  }
}

In my case, object "a" has only one member, object "b", which in turns contains all of the data. I am able to deserialize this using two structures

#[derive(Serialize, Deserialize)]
struct A {
    #[serde(rename = "a")]
    b: B,
}

#[derive(Serialize, Deserialize)]
struct B {
    #[serde(rename = "b")]
    data: REAL_DATA,
}

But it doesn't feel very elegant. I don't want to ever use or interact with struct B, and I would much rather be able to deserialize the data directly into one struct, maybe using something like #[serde(rename="a.b")]. I found someone mentioning the same issue at Flatten · Issue #119 · serde-rs/serde · GitHub, but this is from 2016 and there didn't seem to be a conclusion to this particular comment. Is there a built in solution to this problem that has been created in the intervening years, or are workarounds still the way to go?

1 Like
use serde::{Serialize, Deserialize};
use serde::ser::Serializer;
use serde::de::Deserializer;

type ActualDataType = Vec<u8>;

pub struct A {
    pub data: ActualDataType,
}

impl Serialize for A {
    fn serialize<S: Serializer>(&self, s: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> {
        #[derive(Serialize)]
        struct HelperA<'a> {
            a: HelperB<'a>,
        }
        
        #[derive(Serialize)]
        struct HelperB<'a> {
            b: &'a ActualDataType,
        }
    
        let helper = HelperA {
            a: HelperB {
                b: &self.data,
            }
        };
        HelperA::serialize(&helper, s)
    }
}

impl<'de> Deserialize<'de> for A {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, <D as Deserializer<'de>>::Error> {
        #[derive(Deserialize)]
        struct HelperA {
            a: HelperB,
        }
        
        #[derive(Deserialize)]
        struct HelperB {
            b: ActualDataType,
        }
        
        let helper = HelperA::deserialize(d)?;
        Ok(A {
            data: helper.a.b,
        })
    }
}
3 Likes

Thanks Alice. I was worried that there wouldn't be a one-liner to solve this. Thank you for taking the time to answer my question.