简体   繁体   中英

Deserialize inner enum value from &str to u64 with serde_json

I want to be able to write a color as hex in a json object and then deserialize it to an inner value of type u64 of my enum Color .

At the moment I have an enum looking like this:

#[derive(Deserialize, Serialize)]
pub enum Color {
   Red,
   Green,
   Blue,
   Custom(u64)
}

which I then use in a struct looking like this:

pub struct Config {
    #[serde(rename = "borderColor", deserialize_with = "color_deserialize")]
    pub border_color: Color,
}

Custom deserialize function:

fn color_deserialize<'de, D>(desierializer: D) -> Result<Color, D::Error> 
where
    D: Deserializer<'de>
{
    use serde::de::Error;
    let col = match Color::deserialize(desierializer) {
        Ok(col) => col,
        Err(e) => return Err(format!("Failed to deserilize color: {}", e)).map_err(Error::custom)
    };

    match col {
        Color::Custom(x) => {
            let x_str = &x.to_string();
            let without_prefix = x_str.trim_start_matches("#");
            let res = match u64::from_str_radix(without_prefix, 16) {
                Ok(res) => res,
                Err(e) => return Err(format!("Failed to deserialize color: {}", e)).map_err(Error::custom)
            };
            Ok(Color::Custom(res))
        },
        x => Ok(col)
    }  
}

My understanding of the problem right now is that the derived Deserialize first maps the json value according to the type of the enum (which is u64) before I try to convert it to decimal. So because of this it will break if the json representation is a string rather than number.

How can I keep my variant with the inner type of u64 but represent the color as hex in the json?

You can customize the deserialization for Color itself:

use serde::{de, Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug)]
pub enum Color {
    Red,
    Green,
    Blue,
    #[serde(deserialize_with = "color_deserialize")]
    Custom(u64),
}

#[derive(Deserialize, Serialize, Debug)]
pub struct Config {
    #[serde(rename = "borderColor")]
    pub border_color: Color,
}

fn color_deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
    D: de::Deserializer<'de>,
{
    let s: String = Deserialize::deserialize(deserializer)?;
    let without_prefix = s.trim_start_matches("#");
    match u64::from_str_radix(without_prefix, 16) {
        Ok(res) => Ok(res),
        Err(e) => Err(de::Error::custom(format!(
            "Failed to deserialize color: {}",
            e
        ))),
    }
}

fn main() {
    let c = serde_json::from_str::<Config>(
        r##"{ "borderColor": { "Custom": "#bdcebe" }}"##,
    );
    println!("{:#?}", c);
}

And of course you'll have to implement the other half, ie the serialization, too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM