Как я могу использовать пользовательскую (де) сериализацию Serde для обновления подмножества произвольного ввода? - PullRequest
0 голосов
/ 23 июня 2018

Мне нужно обновить определенные поля произвольного входного файла, не касаясь клавиш или значений, о которых моя программа не знает.

Вот пример входного файла:

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

Я бы хотел обновить alpha.a на 100:

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

Это можно сделать с помощью таких типов, как serde_json::Value и 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.
  • Разрешить добавление, обновление и удаление полей.

1 Ответ

0 голосов
/ 23 июня 2018

Начиная с версии 1.0.34 , вы можете использовать #[serde(flatten)], чтобы захватить все нераспознанные ключи и значения, которые вам не нужны, чтобы создать «всеобщее признание»поле:

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>;

Правильно ли я полагаю, что порядок ключей / форматирования будетбыть полностью отброшенным этим подходом?(По крайней мере, я ожидаю, что alpha будет сериализован первым, независимо от того, какую позицию он первоначально занимал)

Да, порядок клавиш вывода будет основан на комбинации полейпорядок в определении структуры и тип карты, которую вы выбираете.Выше используется BTreeMap, поэтому «другие» ключи будут в алфавитном порядке, но все будут идти после определенных полей.

Вы можете выбрать IndexMap, который сохранит порядок «других»."ключи, хотя они все равно будут относиться к конкретным полям, которые вы добавили:

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

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

См. также:

...