如何使用 Serde 将字段解析为字符串?

How to parse field to string with Serde?

我的 JSON 中有一个自定义字段,它是动态的,需要解析为具有 HashMap 字段的结构,如下所示:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::collections::HashMap;

#[derive(Serialize, Deserialize)]
struct MyStruct {
    field1: String,
    custom: HashMap<String, String>,
}

fn main() {
    let json_string = r#"{"field1":"3","custom":{"custom1":"15000","custom2":"60"}}"#;
    let my_struct = serde_json::from_str::<MyStruct>(json_string).unwrap();
    println!("{}", serde_json::to_string(&my_struct).unwrap());
}

当我的 json 字符串在自定义字段中包含可以轻松解析为字符串的字符串字段时有效。

但问题是我的 json 字符串是:

let json_string_wrong = r#"{"field1":"3","custom":{"custom1":15000,"custom2":"60"}}"#; // Need to parse this

如何在 serde 中处理此类转换?

Serde 提供 serde_json::Value ( reference ) 。它是一个枚举,包含如下数据类型:

pub enum Value {
    /// Represents a JSON null value.
    Null,
    /// Represents a JSON boolean.
    Bool(bool),

    /// Represents a JSON number, whether integer or floating point.
    Number(Number),

    /// Represents a JSON string.
    String(String),

    /// Represents a JSON array.
    Array(Vec<Value>),

    /// Represents a JSON object.
    Object(Map<String, Value>),
}

您可以使用 serde_json::Value 作为 HashMap 的值类型。使用 serde_json::from_value 或使用模式匹配从 serde_json::Value 中提取数据很简单。在您的情况下,我会使用模式匹配,因为只有 Integer 类型会被转换为 String,其余类型将相同。

但是你需要考虑在反序列化之后再增加一个步骤。喜欢

  • 正在为 custom 创建影子字段,将在反序列化后填充。
  • 或构建包含 custom 作为 HashMap<String, String> 的新结构。
  • 添加函数将HashMap<String, Value>转换为HashMap<String, String>,

实现这个特性可以解决你的问题。

trait ToStringStringMap {
    fn to_string_string_map(&self) -> HashMap<String, String>;
}

impl ToStringStringMap for HashMap<String, Value> {
    fn to_string_string_map(&self) -> HashMap<String, String> {
        self.iter()
            .map(|(k, v)| {
                let v = match v.clone() {
                    e @ Value::Number(_) | e @ Value::Bool(_) => e.to_string(),
                    Value::String(s) => s,
                    _ => {
                        println!(r#"Warning : Can not convert field : "{}'s value to String, It will be empty string."#, k);
                        "".to_string()
                    }
                };

                (k.clone(), v)
            })
            .collect()
    }
}

示例Playground

注意: Trait的名字取的不好,欢迎指教。