代码之家  ›  专栏  ›  技术社区  ›  spease

使用serde将键和值列表反序列化为JSON的结构?

  •  4
  • spease  · 技术社区  · 7 年前

    我有这样的JSON:

    {
        "fieldNames": ["MyInt", "MyFloat", "MyString"],
        "fieldValues": [5, 10.0, "hello"],
    }
    

    我想反序列化为如下结构:

    #[derive(Deserialize)]
    struct MyStruct {
        my_int: u64,
        my_float: f64,
        my_string: String,
    }
    

    有没有办法用serde做到这一点?理想情况下,我想要的是:

    #[serde(keys="fieldNames", values="fieldValues")]
    
    1 回复  |  直到 7 年前
        1
  •  5
  •   dtolnay    7 年前

    像这样的事情可能会奏效。这是使用 deserialize_with


    #[macro_use]
    extern crate serde_derive;
    
    extern crate serde;
    extern crate serde_json;
    
    use serde::de::{self, Deserialize, DeserializeOwned, Deserializer};
    use serde_json::Value;
    
    #[derive(Deserialize, Debug)]
    struct Spease(#[serde(deserialize_with = "names_values")] MyStruct);
    
    #[derive(Deserialize, Debug)]
    #[serde(rename_all = "PascalCase")]
    struct MyStruct {
        my_int: u64,
        my_float: f64,
        my_string: String,
    }
    
    fn names_values<'de, T, D>(deserializer: D) -> Result<T, D::Error>
    where
        T: DeserializeOwned,
        D: Deserializer<'de>
    {
        #[derive(Deserialize)]
        struct Helper {
            #[serde(rename = "fieldNames")]
            names: Vec<String>,
            #[serde(rename = "fieldValues")]
            values: Vec<Value>,
        }
    
        // Deserialize a Vec<String> and Vec<Value>.
        let nv = Helper::deserialize(deserializer)?;
    
        // Zip them together into a map.
        let pairs = Value::Object(nv.names.into_iter().zip(nv.values).collect());
    
        // Deserialize the output type T.
        T::deserialize(pairs).map_err(de::Error::custom)
    }
    
    fn main() {
        let j = r#"{
                     "fieldNames": ["MyInt", "MyFloat", "MyString"],
                     "fieldValues": [5, 10.0, "hello"]
                   }"#;
    
        println!("{:?}", serde_json::from_str::<Spease>(j).unwrap());
    }