Лучший способ десериализации списка ключей = значений в Rust - PullRequest
0 голосов
/ 11 января 2019

У меня есть несколько структур, которые необходимо десериализовать из строки, которая представляет собой набор строк с парами ключ-значение, которые представляют атрибуты структуры.

Пример

field1=something
field2=556
field3=true
field4=10.0.0.1

Типы для каждого поля всегда будут одинаковыми, но не всегда присутствуют. Порядок также может измениться.

struct Data {
    field1: Option<String>,
    field2: Option<u32>,
    field3: Option<bool>,
    field4: Option<std::net::Ipv4Addr>
}

Каков наилучший способ сделать это? Должен ли я использовать serde ящик?

Я знаю, что могу сделать это вручную следующим образом (см. https://play.rust -lang.org /? Version = stable & mode = debug & edition = 2018 & gist = e16244e50492aa218217cb44d5f27cfe )

Но как мне обобщить это для нескольких структур?

use std::net::Ipv4Addr;
use std::str::FromStr;

#[derive(Debug)]
struct Data {
    field1: Option<String>,
    field2: Option<u32>,
    field3: Option<bool>,
    field4: Option<Ipv4Addr>,
}

fn main() {
    let mut s = "field1=something
field2=556
field3=true
field4=10.0.0.1"
        .to_string();

    let mut field1 = None;
    let mut field2 = None;
    let mut field3 = None;
    let mut field4 = None;

    let lines: Vec<_> = s.split("\n").collect();

    for line in lines {
        let pair: Vec<_> = line.splitn(2, "=").collect();
        let key = pair[0];
        let value = pair[1];

        match key {
            "field1" => {
                field1 = Some(value.to_owned());
            }
            "field2" => {
                field2 = Some(u32::from_str(value).unwrap());
            }
            "field3" => {
                field3 = match value {
                    "true" => Some(true),
                    "false" => Some(false),
                    _ => None
                };
            }
            "field4" => {
                field4 = Some(Ipv4Addr::from_str(value).unwrap());
            }
            _ => {}
        }
    }

    println!(
        "{:?}",
        Data {
            field1,
            field2,
            field3,
            field4
        }
    );
}

1 Ответ

0 голосов
/ 12 января 2019

Одним из способов обобщения для нескольких целевых структур может быть использование serde .

В случае нестандартного формата должен быть реализован десериализатор, но вместо такой реализации Стоит рассмотреть формат данных, поддерживаемый сообществом, если пользовательский формат является подмножеством или похож на стандартный.

Похоже, что ваш формат является подмножеством формата TOML : в этом случае используйте toml .

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize, Debug)]
struct Data {
    field1: Option<String>,
    field2: Option<u32>,
    field3: Option<bool>,
    field4: Option<std::net::Ipv4Addr>
}

fn main() {
    let serialized = r#"
field1="something"
field2=556
field3=true
field4="10.0.0.1"
"#;

    let deserialized: Data = toml::from_str(&serialized).unwrap();
    println!("{:?}", deserialized);

}

Если ваш формат не совсем "стандартно" совместим, ищите способ преобразования закодированных данных перед десериализацией: например, если field1 и field4 не являются заключенными в кавычки строками, то подстановка шаблона на основе regex май работает:

#[macro_use]
extern crate serde_derive;

use std::borrow::Cow;
use regex::{Captures, Regex};

#[derive(Serialize, Deserialize, Debug)]
struct Data {
    field1: Option<String>,
    field2: Option<u32>,
    field3: Option<bool>,
    field4: Option<std::net::Ipv4Addr>,
}

fn reformat_string(before: &str) -> Cow<str> {
    let matcher : Regex = Regex::new(
            r"(?P<f>field1|field4)=(?P<val>[\w.]+)"
            ).unwrap();

    matcher.replace_all(before, |cap: &Captures| {
        let mut buff = String::new();
        if &cap[1] == "field1" || &cap[1] == "field4" {
            cap.expand("$f='$val'", &mut buff);
        }
        Cow::Owned(buff)
    })
}


fn main() {
    let serialized = r#"
    field1=something
    field2=556
    field3=true
    field4=10.0.0.1
    "#;


    let transformed = reformat_string(serialized);
    let deserialized: Data = toml::from_str(&transformed).unwrap();
    println!("{:?}", deserialized);
}
...