How can I use Serde's custom (de)serialization to update a subset of arbitrary input?

梦想的初衷 提交于 2020-01-24 15:28:26

问题


I need to update specific fields of an arbitrary input file without touching any keys or values that my program does not know about.

Here is an example input file:

{ 
  "alpha": {
    "a": 1,
    "z": 2
  },
  "beta": "b"
}

I'd like to update alpha.a by 100:

{ 
  "alpha": {
    "a": 101,
    "z": 2
  },
  "beta": "b"
}

It is possible to do this with types like serde_json::Value and toml::value::Value, but this code is very cumbersome:

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());
}

I'd much rather use the clean derived syntax:

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());
}

This runs, but it strips out any unknown keys:

{
  "alpha": {
    "a": 2
  }
}

Is there a way I can use the pretty Deserialize and Serialize implementations while still preserving keys and values I don't know about?

An ideal answer would:

  • Work for most Serde formats — I'm showing JSON here but my real code is TOML.
  • Allow adding, updating, and removing fields.

回答1:


As of Serde 1.0.34, you can use #[serde(flatten)] to capture all the unrecognized keys and values you don't care about to create a "catch-all" field:

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,
}

For TOML, you can use a parallel definition of 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)

Yes, the order of the output keys will be based on a combination of the field order in the struct definition and the type of map you choose. Above, a BTreeMap is used, so the "other" keys will be alphabetized, but all come after the specific fields.

You could opt into an IndexMap which would preserve the order of the "other" keys, although they'd still come relative to the specific fields you've added:

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

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

See also:

  • How to (de)serialize a strongly typed JSON dictionary in Serde?
  • Is it possible to flatten sub-object fields while parsing with serde_json?
  • How to deserialize a subfield of a struct from the original struct's JSON with Serde?


来源:https://stackoverflow.com/questions/51002462/how-can-i-use-serdes-custom-deserialization-to-update-a-subset-of-arbitrary-i

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!