【问题标题】:Deserialize TOML string to enum using config-rs使用 config-rs 将 TOML 字符串反序列化为枚举
【发布时间】:2017-12-13 04:55:17
【问题描述】:

我正在使用 config-rs 从 TOML 文件加载配置,并且我想将字符串反序列化为枚举。我尝试使用 serde_derive 的 deserialize_with 功能解决它,但我不知道如何返回合适的错误以满足函数签名。我怎样才能实现它?

我的依赖:

config = "0.7"
serde_derive = "1.0"
serde = "1.0"
toml = "0.4"

用于反序列化枚举 RotationPolicyType 的示例代码:

extern crate config;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::env;
use config::{Config, Environment, File};
use std::path::PathBuf;
use serde;
use serde::de::Deserializer;
use serde::Deserialize;

#[derive(Debug, Deserialize, Clone)]
pub enum RotationPolicyType {
    ByDuration,
    ByDay,
}

#[derive(Debug, Deserialize, Clone)]
pub struct FileConfig {
    pub rotations: i32,
    #[serde(deserialize_with="deserialize_with")]
    pub rotation_policy_type: RotationPolicyType,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
    pub threads: usize,
    pub file_writer: FileConfig,
}

impl Settings {
    pub fn new() -> Self {
        let mut s = Config::new();
        s.merge(File::with_name("config/default")).unwrap();
        s.merge(File::with_name("config/local").required(false))
            .unwrap();
        s.merge(Environment::with_prefix("app")).unwrap();
        s.deserialize().unwrap()
    }
}


fn deserialize_with<D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer {
    let s = String::deserialize(deserializer)?;

    match s.as_ref() {
        "ByDuration" => Ok(RotationPolicyType::ByDuration),
        "ByDay" => Ok(RotationPolicyType::ByDay),
        _ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
    }
}



fn deserialize_with2<'de, D>(deserializer: &'de mut D) -> Result<RotationPolicyType, D::Error> where &'de mut D: Deserializer<'de> {
    let s = String::deserialize(deserializer)?;

    match s.as_ref() {
        "ByDuration" => Ok(RotationPolicyType::ByDuration),
        "ByDay" => Ok(RotationPolicyType::ByDay),
        _ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
    }
}

deserialize_with编译错误:

error[E0106]: missing lifetime specifier
  --> src/settings.rs:30:94
   |
30 |     fn deserialize_with<D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer {
   |                                                                                              ^^^^^^^^^^^^ expected lifetime parameter

error: aborting due to previous error

deserialize_with2的编译错误:

error[E0220]: associated type `Error` not found for `D`
  --> src/settings.rs:42:90
   |
42 |     fn deserialize_with2<'de, D>(deserializer: &'de mut D) -> Result<RotationPolicyType, D::Error> where &'de mut D: Deserializer<'de> {
   |                                                                                          ^^^^^^^^ associated type `Error` not found

error: aborting due to previous error

【问题讨论】:

  • 但我没有成功——你为什么不展示你是如何失败的?代码不能编译吗?代码没有按预期工作吗?发生了什么?您尝试解码什么 TOML 文件?
  • 感谢您的评论。请允许我用我的尝试更新问题。
  • Serde 本身有some examples of what is expected here。具体来说,您似乎想使用Error::custom(....)

标签: rust serde toml


【解决方案1】:

首先,您的示例编译得不够远,无法解决您所描述的错误。下次请注意生成MCVE。让它在https://play.rust-lang.org 上工作的奖励积分(这是可能的,extern crate config 在您的示例中完全没有必要)。

修复所有编译问题后,只需更改函数 API 以匹配建议的 in the serde docs 即可修复您的第一个错误

-fn deserialize_with<     D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer
+fn deserialize_with<'de, D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer<'de>

该错误试图帮助您。它告诉你Deserializer 缺少生命周期参数。

第二个函数告诉你D 没有关联类型Error。只有当D 实现Deserializer&lt;'de&gt; 时它才能拥有。但是您指定&amp;'de mut D 实现Deserializer&lt;'de&gt;。找到这个问题的解决方案留给读者作为练习。

【讨论】:

  • 箱子 config 是使用 config::Config 所必需的,如 Settings::new() 中所示。无法在 play-rust 中重现,因为它没有 crate 配置,但代码不需要任何更改即可重现错误。您必须解决哪些问题?
  • @oli_obk_ker 非常感谢您的建议。我迷失了生命周期并通过引用传递了反序列化器,最后解决方案非常简单。 IDEA 还对我玩了关于serde :: de :: Error :: custom 的知名度的把戏。无论如何,谢谢你的时间!我将发布解决方案以与遇到相同问题的任何人分享。
  • 在操场上复制:play.rust-lang.org/… new 方法对于您的复制来说不是必需的 ;)
【解决方案2】:

按照@oli-obk-ker 的建议,解决方案非常简单:

use std::env;
use config::{Config, File, Environment};
use std::path::PathBuf;
use serde;
use serde::de::Deserializer;
use serde::Deserialize;

pub trait DeserializeWith: Sized {
    fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>;
}

#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]
pub enum RotationPolicyType {
    ByDuration,
    ByDay
}

impl DeserializeWith for RotationPolicyType {
    fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
        let s = String::deserialize(de)?;

        match s.as_ref() {
            "ByDuration" => Ok(RotationPolicyType::ByDuration),
            "ByDay" => Ok(RotationPolicyType::ByDay),
            _ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
        }
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct FileConfig {
    pub rotations: i32,
    #[serde(deserialize_with="RotationPolicyType::deserialize_with")]
    pub rotation_policy_type: RotationPolicyType,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
    pub threads: i32,
    pub file_writer: FileConfig,
}

impl Settings {
    pub fn new() -> Self {
        let mut s = Config::new();
        s.merge(File::with_name("config/default")).unwrap();
        s.merge(File::with_name("config/local").required(false)).unwrap();
        s.merge(Environment::with_prefix("app")).unwrap();
        s.deserialize().unwrap()
    }
}

【讨论】:

    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    • 2013-09-04
    • 2017-04-07
    • 2013-09-09
    相关资源
    最近更新 更多