简体   繁体   English

Rust 反序列化 JSON 成自定义 HashMap<string, google_firestore1::value></string,>

[英]Rust deserialize JSON into custom HashMap<String, google_firestore1::Value>

I just started with Rust and I have some trouble with deserialization.我刚开始使用 Rust 并且在反序列化方面遇到了一些麻烦。

I'm actually trying to use the function ProjectDatabaseDocumentCreateDocumentCall from the following crate google_firestore1 .我实际上是在尝试使用来自以下板条箱google_firestore1的 function ProjectDatabaseDocumentCreateDocumentCall I want to populate the field fields of the struct Document .我想填充 struct Document的字段fields The documentation of the struct is clear, it's expecting a HashMap<String, google_firestore1::Value> as a value.结构的文档很清楚,它期望一个HashMap<String, google_firestore1::Value>作为一个值。

The question is, how can I deserialize a JSON string to a HashMap<String, google_firestore1::Value> ?问题是,如何将 JSON 字符串反序列化为HashMap<String, google_firestore1::Value>

Here is the code I wrote for the moment:这是我暂时写的代码:

extern crate google_firestore1 as firestore1;

use google_firestore1::Document;
use std::collections::HashMap;
use serde_json;

pub fn go() {

  let _my_doc = Document::default();

  let test = "{\"test\":\"test\", \"myarray\": [1]}";

  // Working perfectly fine
  let _working: HashMap<String, serde_json::Value> = serde_json::from_str(test).unwrap();

  // Not working
  let _not_working: HashMap<String, firestore1::Value> = serde_json::from_str(test).unwrap();

  // Later I want to do the following
  // _my_doc.fields = _not_working
}

Obvsiouly this is not working, and it crashes with the following error. Obvsiouly 这不起作用,它崩溃并出现以下错误。

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: string \"test\", expected struct Value", line: 1, column: 14)', src/firestore.rs:17:85
stack backtrace:

Of course, I noticed that serde_json::Value and firestore1::Value are not the same Struct.当然,我注意到serde_json::Valuefirestore1::Value不是同一个 Struct。 But I gave a look at the source code and it seems that firestore1::Value is implementing the Deserialize trait.但是我看了一下源代码,似乎firestore1::Value正在实现 Deserialize 特征。

So why is it not working?那么为什么它不起作用呢? In this case, do I need to iterate over the first HashMap and deserialize serde_json::Value to firestore1::Value again?在这种情况下,我是否需要遍历第一个 HashMap 并将serde_json::Value反序列化为firestore1::Value Is there a cleaner way to do what I want?有没有更清洁的方法来做我想做的事?

Thanks for your answer !感谢您的回答 !

The definition of the firestore1::Value is: firestore1::Value的定义是:

/// A message that can hold any of the supported value types.
/// 
/// This type is not used in any activity, and only used as *part* of another schema.
/// 
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct Value {
    /// A bytes value.
    /// 
    /// Must not exceed 1 MiB - 89 bytes.
    /// Only the first 1,500 bytes are considered by queries.
    #[serde(rename="bytesValue")]
    pub bytes_value: Option<String>,
    /// A timestamp value.
    /// 
    /// Precise only to microseconds. When stored, any additional precision is
    /// rounded down.
    #[serde(rename="timestampValue")]
    pub timestamp_value: Option<String>,

    ...

}

This means each entry for a firestore1::Value must be an object.这意味着firestore1::Value的每个条目都必须是 object。 I suspect that only one of the fields would actually be set, corresponding to the actual type of the value (as they're all optional).怀疑实际上只会设置一个字段,对应于值的实际类型(因为它们都是可选的)。

So your json would need to be something like:所以你的 json 需要是这样的:

let test = r#"{
  "test":{"stringValue":"test"},
  "myarray": {
     "arrayValue":{"values":[{"integerValue":1}]}
  }
}"#;

This is pretty ugly, so if you're doing a lot of your own JSON to firestore conversations, I'd probably write some helpers to convert from the serde_json::Value to firestore1::Value .这非常难看,所以如果你自己做很多 JSON 来进行 firestore 对话,我可能会写一些帮助程序来从serde_json::Value转换为firestore1::Value

It would probably look something like this:它可能看起来像这样:

fn my_firestore_from_json(v:serde_json::Value) -> firestore1::Value {
  match v {
    serde_json::Value::Null => firestore::Value {
      // I don't know why this is a Option<String>
      null_value: Some("".to_string),
      ..Default::default(),
    },
    serde_json::Value::Bool(b) => firestore::Value {
      bool_value: Some(b),
      ..Default::default(),
    },
    // Implement this
    serde_json::Value::Number(n) => my_firestore_number(n),
    serde_json::Value::String(s) => firestore::Value {
      string_value: Some(s),
      ..Default::default(),
    },
    serde_json::Value::Array(v) => firestore::Value {
      array_value:
        Some(firestore1::ArrayValue{
            values:v.into_iter().map(my_firestore_from_json)
        }),
      ..Default::default(),
    },
    // Implement this
    serde_json::Value::Object(d) => my_firststore_object(/* something */) 
  }
}

This would be a bit neater if there were various implementations of From<T> for the firestore1::Value , but using the implementation of Default makes this not too ugly.如果有针对firestore1::ValueFrom<T>的各种实现,这会更简洁一些,但是使用Default的实现不会太难看。

It is also worth noting that not all firebase types are created here, since the types expressed in serde_json are different from those supported by firebase.还值得注意的是,并不是所有的 firebase 类型都在这里创建,因为 serde_json 表示的类型与 firebase 支持的类型不同。

Anyway this allows you to use your JSON as written by doing something like:无论如何,这允许您使用您的 JSON 通过执行以下操作来编写:

let test = "{\"test\":\"test\", \"myarray\": [1]}";
let working: HashMap<String, serde_json::Value> = serde_json::from_str(test).unwrap();
let value_map: HashMap<String, firestore1::Value> = working.iter().map(|(k,v)| (k, my_firestore_from_json(v)).collect();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM