Serde deserialize struct with dynamically prefixed fields

I’ve posted this on https://github.com/serde-rs/serde/issues/970#issuecomment-438153519

I’m re-posting here because I’ve gone through the docs and haven’t found a solution that I can grok.

I’ve looked:

I’m stuck using a 3rd party API that uses a convention in the same vein, but doesn’t fit the serde_with use case; or perhaps I’m not smart enough to see how it fits.

I have a response with the following structure:

{
  "something": [
  {
    "name": "foo",
    "annoying": [
      { "name": "thing1", "foo_cow": 1, "foo_dog": 2, "foo_cat": 3 },
      { "name": "thing2", "foo_cow": 2, "foo_dog": 3, "foo_cat": 4},
    ]
  },
  {
    "name": "bar",
    "annoying": [
      { "name": "thing1", "bar_cow": 1, "bar_dog": 2, "bar_cat": 3 },
      { "name": "thing2", "bar_cow": 2, "bar_dog": 3, "bar_cat": 4},
    ]
  },
  {
    "name": "baz",
    "annoying": [
      { "name": "thing1", "baz_cow": 1, "baz_dog": 2, "baz_cat": 3 },
      { "name": "thing2", "baz_cow": 2, "baz_dog": 3, "baz_cat": 4},
    ]
  }]
}

I want to deserialize into:

#[derive(Deserialize)]
pub struct Member {
  name: String,
  cow: u16,
  dog: u16,
  cat: u16,
}
#[derive(Deserialize)]
pub struct Group {
    name: String,
    #[serde(rename = "annoying")]
    members: Vec<Member>
}
#[derive(Deserialize)]
pub struct Collection {
  #[serde(rename = "something")]
  items: Vec<Group>  
}

Is there a macro to deserialize based on a regex field match? Any help would be sanity saving :smile:

Unfortunately this same API also has this return…

{
    "name": "some-report",
    "category_lists": [
        {
            "name": "ABC",
            "x_one": 123,
            "x_two": 345,
            "x_three": 678,
        },
        {
            "name": "ABC",
            "y_one": 123,
            "y_two": 345,
            "y_three": 678,
        },
        {
            "name": "ABC",
            "z_one": 123,
            "z_two": 345,
            "z_three": 678,
        },
    ]
}

Which I would like to deserialize into this:

struct Report {
    categories: Vec<Category>
}

struct Category {
    name: String,
    one: u32,
    two: u32,
    three: u32,
}

Here, unlike the previous example the possibilities are known ahead of time (x,y,z) but they’re still rather dynamic and don’t matter to the output.

I found your question really interesting so i started digging, and there seems to be a way to deserialize without knowing the field names in advance, by using deserialize_map instead of deserialize_struct. I adapted the example from serde’s documentation that you linked to get the const FIELDS: &'static [&'static str] = &["secs", "nanos"]; out of there, and it worked:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=375788389fb1a8657eedfe660b380954

Edit: deserializer for your struct type:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=5af99c81a23606beca806ddb0c1e5fb4
The input data you provided uses trailing commas though, which cause an error from serde. I removed them.

@mmmmib Thank you :pray: . I completely missed the part about the impl Field. I think I was distracted by the comment about it being automatically derived.

Here is the full code I used to deserialize the response found here: http://www.demographicspro.com/api/7/resources/sample-get-codes-response

Hopefully this is helpful and many thanks to @mmmmib.

1 Like