如何使用 Serde 的自定义(反)序列化来更新任意输入的子集?
How can I use Serde's custom (de)serialization to update a subset of arbitrary input?
我需要更新任意输入文件的特定字段而不触及我的程序不知道的任何键或值。
这是一个示例输入文件:
{
"alpha": {
"a": 1,
"z": 2
},
"beta": "b"
}
我想将 alpha.a
更新到 100:
{
"alpha": {
"a": 101,
"z": 2
},
"beta": "b"
}
用serde_json::Value
and toml::value::Value
这样的类型是可以的,但是这段代码很麻烦:
extern crate serde; // 1.0.66
extern crate serde_json; // 1.0.21
use serde_json::Value;
fn main() {
let input = r#"{
"alpha": {
"a": 1,
"z": 2
},
"beta": "b"
}"#;
let mut to_change: Value = serde_json::from_str(input).unwrap();
{
let obj = to_change.as_object_mut().unwrap();
let alpha = obj.get_mut("alpha").unwrap();
let obj = alpha.as_object_mut().unwrap();
let num = {
let a = obj.get("a").unwrap();
let mut num = a.as_i64().unwrap();
num += 100;
num
};
obj.insert("a".into(), Value::Number(num.into()));
}
println!("{}", serde_json::to_string_pretty(&to_change).unwrap());
}
我更愿意使用干净的派生语法:
extern crate serde; // 1.0.66
#[macro_use]
extern crate serde_derive; // 1.0.66
extern crate serde_json; // 1.0.21
#[derive(Debug, Deserialize, Serialize)]
struct WhatICareAbout {
alpha: Alpha,
}
#[derive(Debug, Deserialize, Serialize)]
struct Alpha {
a: i32,
}
fn main() {
let input = r#"{
"alpha": {
"a": 1,
"z": 2
},
"beta": "b"
}"#;
let mut subobject: WhatICareAbout = serde_json::from_str(input).unwrap();
subobject.alpha.a += 1;
println!("{}", serde_json::to_string_pretty(&subobject).unwrap());
}
这会运行,但会删除所有未知键:
{
"alpha": {
"a": 2
}
}
有没有一种方法可以使用漂亮的 Deserialize
和 Serialize
实现,同时仍然保留我不知道的键和值?
理想的答案是:
- 适用于大多数 Serde 格式 — 我在这里展示 JSON 但我的真实代码是 TOML。
- 允许添加、更新和删除字段。
As of Serde 1.0.34, you can use #[serde(flatten)]
以捕获所有您不关心的无法识别的键和值来创建一个 "catch-all" 字段:
extern crate serde; // 1.0.66
#[macro_use]
extern crate serde_derive; // 1.0.66
extern crate serde_json; // 1.0.21
type Other = serde_json::Map<String, serde_json::Value>;
#[derive(Debug, Deserialize, Serialize)]
struct WhatICareAbout {
alpha: Alpha,
#[serde(flatten)]
other: Other,
}
#[derive(Debug, Deserialize, Serialize)]
struct Alpha {
a: i32,
#[serde(flatten)]
other: Other,
}
对于 TOML,您可以使用 Other
:
的并行定义
type Other = std::collections::BTreeMap<String, Value>;
Am I correct in assuming that the order of keys/formatting will be completely discarded by this approach? (At the very least, I expect alpha
to be serialized first, regardless of the position it originally occupied)
是的,输出键的顺序将基于结构定义中的字段顺序和您选择的映射类型的组合。上面使用了 BTreeMap
,因此 "other" 键将按字母顺序排列,但都在特定字段之后。
您可以选择 IndexMap
来保留 "other" 键的顺序,尽管它们仍然与您添加的特定字段有关:
extern crate indexmap; // 0.4.1 + features = ["serde-1"]
type Other = indexmap::IndexMap<String, serde_json::Value>;
另请参阅:
我需要更新任意输入文件的特定字段而不触及我的程序不知道的任何键或值。
这是一个示例输入文件:
{
"alpha": {
"a": 1,
"z": 2
},
"beta": "b"
}
我想将 alpha.a
更新到 100:
{
"alpha": {
"a": 101,
"z": 2
},
"beta": "b"
}
用serde_json::Value
and toml::value::Value
这样的类型是可以的,但是这段代码很麻烦:
extern crate serde; // 1.0.66
extern crate serde_json; // 1.0.21
use serde_json::Value;
fn main() {
let input = r#"{
"alpha": {
"a": 1,
"z": 2
},
"beta": "b"
}"#;
let mut to_change: Value = serde_json::from_str(input).unwrap();
{
let obj = to_change.as_object_mut().unwrap();
let alpha = obj.get_mut("alpha").unwrap();
let obj = alpha.as_object_mut().unwrap();
let num = {
let a = obj.get("a").unwrap();
let mut num = a.as_i64().unwrap();
num += 100;
num
};
obj.insert("a".into(), Value::Number(num.into()));
}
println!("{}", serde_json::to_string_pretty(&to_change).unwrap());
}
我更愿意使用干净的派生语法:
extern crate serde; // 1.0.66
#[macro_use]
extern crate serde_derive; // 1.0.66
extern crate serde_json; // 1.0.21
#[derive(Debug, Deserialize, Serialize)]
struct WhatICareAbout {
alpha: Alpha,
}
#[derive(Debug, Deserialize, Serialize)]
struct Alpha {
a: i32,
}
fn main() {
let input = r#"{
"alpha": {
"a": 1,
"z": 2
},
"beta": "b"
}"#;
let mut subobject: WhatICareAbout = serde_json::from_str(input).unwrap();
subobject.alpha.a += 1;
println!("{}", serde_json::to_string_pretty(&subobject).unwrap());
}
这会运行,但会删除所有未知键:
{
"alpha": {
"a": 2
}
}
有没有一种方法可以使用漂亮的 Deserialize
和 Serialize
实现,同时仍然保留我不知道的键和值?
理想的答案是:
- 适用于大多数 Serde 格式 — 我在这里展示 JSON 但我的真实代码是 TOML。
- 允许添加、更新和删除字段。
As of Serde 1.0.34, you can use #[serde(flatten)]
以捕获所有您不关心的无法识别的键和值来创建一个 "catch-all" 字段:
extern crate serde; // 1.0.66
#[macro_use]
extern crate serde_derive; // 1.0.66
extern crate serde_json; // 1.0.21
type Other = serde_json::Map<String, serde_json::Value>;
#[derive(Debug, Deserialize, Serialize)]
struct WhatICareAbout {
alpha: Alpha,
#[serde(flatten)]
other: Other,
}
#[derive(Debug, Deserialize, Serialize)]
struct Alpha {
a: i32,
#[serde(flatten)]
other: Other,
}
对于 TOML,您可以使用 Other
:
type Other = std::collections::BTreeMap<String, Value>;
Am I correct in assuming that the order of keys/formatting will be completely discarded by this approach? (At the very least, I expect
alpha
to be serialized first, regardless of the position it originally occupied)
是的,输出键的顺序将基于结构定义中的字段顺序和您选择的映射类型的组合。上面使用了 BTreeMap
,因此 "other" 键将按字母顺序排列,但都在特定字段之后。
您可以选择 IndexMap
来保留 "other" 键的顺序,尽管它们仍然与您添加的特定字段有关:
extern crate indexmap; // 0.4.1 + features = ["serde-1"]
type Other = indexmap::IndexMap<String, serde_json::Value>;
另请参阅: