Rust 如何使用Serde';s自定义(反)序列化以更新任意输入的子集?

Rust 如何使用Serde';s自定义(反)序列化以更新任意输入的子集?,rust,serde,Rust,Serde,我需要更新任意输入文件的特定字段,而不必触摸我的程序不知道的任何键或值 以下是一个示例输入文件: { "alpha": { "a": 1, "z": 2 }, "beta": "b" } 我想更新alpha.a100: { "alpha": { "a": 101, "z": 2 }, "beta": "b" } 可以使用和等类型执行此操作,但此代码非常繁琐: extern crate serde; // 1.0.66 extern

我需要更新任意输入文件的特定字段,而不必触摸我的程序不知道的任何键或值

以下是一个示例输入文件:

{ 
  "alpha": {
    "a": 1,
    "z": 2
  },
  "beta": "b"
}
我想更新
alpha.a
100:

{ 
  "alpha": {
    "a": 101,
    "z": 2
  },
  "beta": "b"
}
可以使用和等类型执行此操作,但此代码非常繁琐:

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
  }
}
有没有一种方法可以在保留我不知道的键和值的同时,使用pretty
反序列化
序列化
实现

理想的答案是:

  • 适用于大多数Serde格式-我在这里展示JSON,但我真正的代码是TOML
  • 允许添加、更新和删除字段
,您可以使用来捕获所有您不关心的无法识别的键和值,以创建一个“全部捕获”字段:


我是否正确地假设键/格式的顺序将被这种方法完全丢弃?(至少,我希望
alpha
首先被序列化,不管它最初占据什么位置)

是的,输出键的顺序将基于结构定义中的字段顺序和您选择的映射类型的组合。上面,使用了一个
BTreeMap
,因此“其他”键将按字母顺序排列,但都位于特定字段之后

您可以选择一个
IndexMap
,它将保留“其他”键的顺序,尽管它们仍然与您添加的特定字段相关:

extern crate indexmap; // 0.4.1 + features = ["serde-1"]

type Other = indexmap::IndexMap<String, serde_json::Value>;
extern板条箱索引映射;//0.4.1+功能=[“serde-1”]
键入Other=indexmap::indexmap;
另见:


我认为通过这种方法,键/格式的顺序将被完全丢弃,这是正确的吗?(至少,我希望
alpha
首先被序列化,而不管它最初占据什么位置)如果是这样的话,请注意,因为在提交的文件上使用这样的“脚本”可能会导致大的更改。@MatthieuM。有趣的一点!这通常与我编辑Cargo.toml的情况有关,Cargo.toml通常是一个提交给源代码管理的文件,由人工编辑。在我的例子中,这并不重要,因为它是用于操场的。当我尝试此操作时,我得到了
错误:未知的serde variant属性`flatte
,即使我有serde v1.0.70。如果您能在答案中添加必要的
use
语句,那就太好了。@PhilippLudwig板条箱语句和版本在问题中,但如果不明显的话,我可以复制它们。@Shepmaster我的错,我想这个功能也可以在序列化过程中使用(例如,展平JSON结构)。最后,我简化了我的数据结构。
type Other = std::collections::BTreeMap<String, Value>;
extern crate indexmap; // 0.4.1 + features = ["serde-1"]

type Other = indexmap::IndexMap<String, serde_json::Value>;