在 Rust 中反序列化 key=value 字符串列表的最佳方法

问题描述 投票:0回答:2

我有多个结构需要从字符串反序列化,该字符串是一堆带有代表结构属性的键值对的行。

示例

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
        }
    );
}
parsing rust serde
2个回答
2
投票

泛化多个目标结构的一种方法可能是使用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);
}

0
投票

有一个 serde crate 可以做到这一点,但它似乎没有发布。让我们复活吧?

© www.soinside.com 2019 - 2024. All rights reserved.