serde-rs / serde

Serialization framework for Rust
https://serde.rs/
Apache License 2.0
8.86k stars 749 forks source link

Deserializing runtime types #2609

Open racradi opened 10 months ago

racradi commented 10 months ago

Sorry if this is not the right place to ask, but I've been stuck with this problem for a while and I'm not sure if it's possible to achieve this using Serde.

I have what is effectively a dynamic (runtime) representation of a struct. I want to use this representation to deserialize some data. To my understanding, the typical solution would be to treat it as a map and use deserialize_map alongside a custom Visitor, however I cannot do that as the serialization format I'm using has length-prefixed maps. The data I'm interested in deserializing does not treat these types as maps, and so there's no length prefix. Although serialize_struct is pretty much what I want, I can't use that either as the names of the members are not &'static str.

More concretely, here's a condensed example:

enum Kind {
    Struct,
    Number,
}

// Using this representation of the struct, I know the types and
// names of each member.
struct MyStructRepr {
    name: String,
    members: Vec<(String, Kind)>,
}

// The actual data into which I want to deserialize the input.
enum Data {
    Struct {
        members: Vec<Data>,
    },
    Number(usize),
    String(String),
}

Is this doable with Serde?

Rulexec commented 10 months ago

You can try to do it in two steps. If I understood the problem correctly, you can try to Implement Deserialize for MyStructRepr, which closely matches original data. Then implement Deserialize for Data, which calls MyStructRepr::deserialize(deserializer)?, then map intermediate representation to wanted one. Or even maybe without two Deserialize impls, your custom Visitor can return type which is just extracts data which you can map later to expected result.

racradi commented 10 months ago

@Rulexec Unfortunately that doesn't address my issue.

MyStructRepr is just a way to represent the definition of a type. For example, consider the following type:

struct Foo {
    my_member: i32,
}

The same type could instead be represented as:

let foo = MyStructRepr {
   name: "Foo".to_string(),
   members: vec![("my_member".to_string(), Kind::Number)]
};

The point here is that the definition of the type is not available at compile time, as these are dynamic types created at runtime. MyStructRepr should only be used as a way to drive the deserializer, i.e. tell the deserializer what comes next. This leads me back to my initial problem: driving a deserializer based on runtime information.

I cannot use deserialize_struct, as it requires that the names of all member fields are &'static str (something they are not in my case), and I cannot use deserialize_map as the serialization format I am using encodes maps differently from structs. I can make deserialize_struct work by leaking the strings, but I'd rather not do that.

A similar thing was discussed in #2043, but unfortunately the solution there doesn't help my use case.

Mingun commented 10 months ago

MyStructRepr should only be used as a way to drive the deserializer,

This looks like MyStructRepr is itself should be (part of) deserializer, but I under impression that you want to deserialize that type from another deserializer.

The deserialize_* methods called by the deserializable types, so if MyStructRepr is a deserializer, then what you want should be possible.

racradi commented 10 months ago

@Mingun

under impression that you want to deserialize that type from another deserializer.

Yes, that is what I'm trying to achieve. MyStructRepr is not a deserializer itself, it just contains information about the type that should be used to drive another deserializer. To make things more tricky, it has to work with different deserializers, including e.g. serde_json.