Custom Serde serialize / deserialize


#1

I have to serialize / deserialize data with serde which cannot be handled by the default routines. As such I tried to implement the Serialize and Deserialize for the data structure. While I got it working for serialize (not sure if it is the correct way), I fail to find a solution for deserialize.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

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

pub static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf";

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WebElement {
    #[serde(rename="element-6066-11e4-a52e-4f735466cecf")]
    pub id: String
}

#[derive(Debug, PartialEq)]
pub enum PointerOrigin {
    Viewport,
    Pointer,
    Element(WebElement),
}

impl Serialize for PointerOrigin {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        match *self {
            PointerOrigin::Pointer => serializer.serialize_str("pointer"),
            PointerOrigin::Viewport => serializer.serialize_str("viewport"),
            PointerOrigin::Element(ref  element) => {
                serde::Serialize::serialize(element, serializer)
            }
        }
    }
}

impl<'de> Deserialize<'de> for PointerOrigin {
    fn deserialize<D>(deserializer: D) -> Result<PointerOrigin, D::Error>
        where D: Deserializer<'de>
    {
        // ?
    }
}

fn main() {
    let pointer = PointerOrigin::Element(WebElement { id: "elem".into() });

    let serialized = serde_json::to_string(&pointer).unwrap();

    // Prints serialized = {"element-6066-11e4-a52e-4f735466cecf":"elem"}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a PointerOrigin.
    let deserialized: PointerOrigin = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized
    println!("deserialized = {:?}", deserialized);
}

Could anyone please advise in how the deserializer has to be implemented?


#2

Does the example in the guide help?


#3

It looks like you want the three PointerOrigin representations to be:

"viewport"
"pointer"
{"element-6066-11e4-a52e-4f735466cecf":"..."}

That would be the following enum.

#[derive(Serialize, Deserialize)]
enum PointerOrigin {
    #[serde(rename = "viewport")]
    Viewport,
    #[serde(rename = "pointer")]
    Pointer,
    #[serde(rename = "element-6066-11e4-a52e-4f735466cecf")]
    Element(String),
}

#4

WebElement needs to stay as its own type (struct) because it is also used in other data structures in that project, and not only in PointerOrigin. @dtolnay, so your enum proposal would sadly not work.

The examples I found so far or I was pointed to (also applies to comment 2 in this thread) are a bit hard to oversee what’s really necessary. I assume I would still have to use a custom Serializer and Deserializer implementation? And in that regards a Visitor implementation is necessary?


#5

The separate WebElement version of the same thing would be:

#[derive(Serialize, Deserialize)]
struct WebElement {
    #[serde(rename = "element-6066-11e4-a52e-4f735466cecf")]
    id: String
}

#[derive(Serialize, Deserialize)]
enum PointerOrigin {
    #[serde(rename = "viewport")]
    Viewport,
    #[serde(rename = "pointer")]
    Pointer,
    #[serde(
        rename = "element-6066-11e4-a52e-4f735466cecf",
        serialize_with = "serialize_webelement_id",
        deserialize_with = "deserialize_webelement_id")]
    Element(WebElement),
}

fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer
{
    element.id.serialize(serializer)
}

fn deserialize_webelement_id<'de, D>(deserializer: D) -> Result<WebElement, D::Error>
    where D: Deserializer<'de>
{
    String::deserialize(deserializer).map(|id| WebElement { id })
}

#6

Thank you a lot! I’m kinda impressed that this can be done with such a minimal effort. I updated my local code, and the appropriate tests for the referenced data structures all work fine now. I hope that I can solve the other remaining issues the same way.


#7

@dtolnay it would be good to have examples for serialize_with and deserialize_with available somewhere. The serde docs sadly miss them, or I couldn’t find them.