Десериализуйте внутреннее значение перечисления от & str до u64 с помощью serde_json - PullRequest
2 голосов
/ 22 января 2020

Я хочу иметь возможность записать цвет как шестнадцатеричный объект json и затем десериализовать его во внутреннее значение типа u64 моего перечисления Color.

На данный момент у меня есть перечисление, похожее на это:

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

, которое я затем использую в структуре, похожей на это:

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

Пользовательская функция десериализации:

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)
    }  
}

Мое понимание проблемы сейчас заключается в том, что производное Deserialize сначала отображает значение json в соответствии с типом перечисления (которое является u64), прежде чем я пытаюсь преобразовать его в десятичное , Поэтому из-за этого он сломается, если представление json будет строкой, а не числом.

Как я могу сохранить свой вариант с внутренним типом u64, но представить цвет как шестнадцатеричный в json

1 Ответ

1 голос
/ 22 января 2020

Вы можете настроить десериализацию для самого Color:

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);
}

И, конечно, вам придется реализовать другую половину, то есть сериализацию.

...