读取 Rust 中整数包含下划线的 yaml

Pas*_*ten 6 rust serde

我希望能够读取包含下划线的整数(千位分隔符)

- instrument: 5_000_000
  other_field: this string contains an _
- instrument: 5_000_000
  other_field: this string contains an _
Run Code Online (Sandbox Code Playgroud)

使用 serde_yaml 这怎么可能?

use*_*968 6

正如我在评论中所说,就 yaml 反序列化器而言,文本“5_000_000”始终是字符串,而不是整数。所以你需要告诉serde这个领域需要特殊对待。您可以按照注释中的描述创建一个FooSerialized结构(这会重复很多定义),或者使用该deserialize_with属性来自定义字段反序列化:

use serde::{de, Deserialize};

#[derive(Deserialize, Debug)]
pub struct Foo {
    #[serde(deserialize_with = "deserialize_underscored_integer")]
    pub instrument: u64,
    pub other_field: String,
}

fn deserialize_underscored_integer<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
    D: de::Deserializer<'de>,
    T: std::str::FromStr,
{
    // First, deserialize the value as a string (which might fail...)
    let s: String = de::Deserialize::deserialize(deserializer)?;

    // next, filter out the underscores (and invalid chars while we are at it),
    // collect the remaining chars into a new string, parse that as an integer
    // and return that
    s.chars()
        .filter_map(|c| match c {
            c @ '0'..='9' => Some(Ok(c)),
            '_' => None,
            _ => Some(Err(de::Error::custom("invalid char in string"))),
        })
        .collect::<Result<String, _>>()?
        .parse()
        .map_err(|_: <T as std::str::FromStr>::Err| {
            de::Error::custom("string does not represent an integer")
        })
}

fn main() {
    // This will succeed
    let inp = r#"
- instrument: 1_2_34_567_8_9____0
  other_field: this string contains an _
- instrument: 5_000_000
  other_field: this string contains an _
"#;
    println!("{:?}", serde_yaml::from_str::<Vec<Foo>>(inp).unwrap());

    // This will fail because its not a integer
    let inp = r#"
- instrument: 5000 abcdef
  other_field: this string contains some other stuff
"#;
    println!("{:?}", serde_yaml::from_str::<Vec<Foo>>(inp).unwrap_err());

    // This looks like an integer but is not a u64
    let inp = r#"
- instrument: 5_000_000_000_000_000_000_000
  other_field: this string is too large to be a u64
"#;
    println!("{:?}", serde_yaml::from_str::<Vec<Foo>>(inp).unwrap_err());
}
Run Code Online (Sandbox Code Playgroud)