Rust 将 JSON 反序列化为自定义 HashMap<String, google_firestore1::Value>
Rust deserialize JSON into custom HashMap<String, google_firestore1::Value>
我刚开始使用 Rust,在反序列化方面遇到了一些麻烦。
我实际上正在尝试使用结构的函数 ProjectDatabaseDocumentCreateDocumentCall from the following crate google_firestore1. I want to populate the field fields
of the struct Document
. The documentation 很清楚,它期望 HashMap<String, google_firestore1::Value>
作为值。
问题是,如何将 JSON 字符串反序列化为 HashMap<String, google_firestore1::Value>
?
这是我暂时写的代码:
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
}
显然这不起作用,它因以下错误而崩溃。
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:
当然,我注意到serde_json::Value
和firestore1::Value
不是同一个Struct。
但是我看了一下 source code ,似乎 firestore1::Value
正在实现反序列化特征。
为什么它不起作用?在这种情况下,我是否需要遍历第一个 HashMap 并再次将 serde_json::Value
反序列化为 firestore1::Value
?有没有更简洁的方法来做我想做的事?
感谢您的回答!
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>,
...
}
这意味着 firestore1::Value
的每个条目都必须是一个对象。
我怀疑实际上只有一个字段会被设置,对应
到值的实际类型(因为它们都是可选的)。
所以你的 json 需要像这样:
let test = r#"{
"test":{"stringValue":"test"},
"myarray": {
"arrayValue":{"values":[{"integerValue":1}]}
}
}"#;
这很难看,所以如果你自己做很多 JSON 来恢复对话,我可能会写一些帮助程序来从 serde_json::Value
转换为 firestore1::Value
.
它可能看起来像这样:
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 */)
}
}
如果 firestore1::Value
有多种 From<T>
的实现,这会更整洁一些,但是使用的实现
Default
让这看起来不太难看。
还值得注意的是,并不是所有的 firebase 类型都在这里创建,
因为 serde_json 中表达的类型与 firebase 支持的类型不同。
无论如何,这允许您使用您编写的 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();
我刚开始使用 Rust,在反序列化方面遇到了一些麻烦。
我实际上正在尝试使用结构的函数 ProjectDatabaseDocumentCreateDocumentCall from the following crate google_firestore1. I want to populate the field fields
of the struct Document
. The documentation 很清楚,它期望 HashMap<String, google_firestore1::Value>
作为值。
问题是,如何将 JSON 字符串反序列化为 HashMap<String, google_firestore1::Value>
?
这是我暂时写的代码:
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
}
显然这不起作用,它因以下错误而崩溃。
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:
当然,我注意到serde_json::Value
和firestore1::Value
不是同一个Struct。
但是我看了一下 source code ,似乎 firestore1::Value
正在实现反序列化特征。
为什么它不起作用?在这种情况下,我是否需要遍历第一个 HashMap 并再次将 serde_json::Value
反序列化为 firestore1::Value
?有没有更简洁的方法来做我想做的事?
感谢您的回答!
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>,
...
}
这意味着 firestore1::Value
的每个条目都必须是一个对象。
我怀疑实际上只有一个字段会被设置,对应
到值的实际类型(因为它们都是可选的)。
所以你的 json 需要像这样:
let test = r#"{
"test":{"stringValue":"test"},
"myarray": {
"arrayValue":{"values":[{"integerValue":1}]}
}
}"#;
这很难看,所以如果你自己做很多 JSON 来恢复对话,我可能会写一些帮助程序来从 serde_json::Value
转换为 firestore1::Value
.
它可能看起来像这样:
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 */)
}
}
如果 firestore1::Value
有多种 From<T>
的实现,这会更整洁一些,但是使用的实现
Default
让这看起来不太难看。
还值得注意的是,并不是所有的 firebase 类型都在这里创建, 因为 serde_json 中表达的类型与 firebase 支持的类型不同。
无论如何,这允许您使用您编写的 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();