有没有一种方法可以使用Rust的serde/serde_json来“修补”一个结构体?

3
有没有一种相对简单的方法使用serde/serde_json来接受现有结构体并仅更新JSON中存在的字段?这基本上相当于在运行时实现默认值而不是通过实现Default trait或生成默认值函数来编译时设置默认值。
当您拥有RESTful API并且只想修改指定字段并使未指定字段保持不变时,这似乎是一个非常常见的用例。
我可以通过将其反序列化为动态Value类型,然后执行大型匹配或if/else块来更新字段,但这很冗长和丑陋。我想知道serde是否有任何处理此操作的内容。

1
相信 serde 只关注于序列化和反序列化;补丁/合并/差异不是其功能集的一部分。像 json_patch 这样的板条箱可能会对您有所帮助,但我不太确定。更强大的策略是定义一个带有 Option 字段的 *Update 结构,并以这种方式进行反序列化。这可能需要更多的工作,但我发现它在长期运行中更加灵活。 - kmdreko
我觉得 json_patch 可能基本上可以满足我的需求,尽管它没有 serde 那么直接。 - Adam Ierymenko
我认为Serde直接整合这个功能是有意义的。Serde已经有了将数据反序列化到现有结构体的方法,因此它已经具备了更新的能力。它只需要在缺少字段时不会出现错误。这里是一个示例:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bccf10311e413af139f3fee957a1e747 - Max Murphy
1个回答

4

Serde不支持这样的功能。如果我需要这样做,我可能会创建一个并行结构,其中所有字段都是可选的:

use serde::Deserialize;

#[derive(Deserialize)]
pub struct MyData {
    foo: String,
    bar: u64,
    wibble: bool,
}

#[derive(Deserialize)]
pub struct MyDataPatch {
    foo: Option<String>,
    bar: Option<u64>,
    wibble: Option<bool>,
}

impl MyData {
    pub fn patch(&mut self, update: MyDataPatch) {
        if let Some(foo) = update.foo {
            self.foo = foo;
        }
        if let Some(bar) = update.bar {
            self.bar = bar;
        }
        if let Some(wibble) = update.wibble {
            self.wibble = wibble;
        }
    }
}

这里存在更多的代码重复,但运行时开销较少。

如果我有很多这样的代码,那么我会使用宏来生成所有的代码,而不是手写。你可以使用可派生特性来实现这一点,例如:

trait Patch {
    type Patch; // Self with all optional fields
    fn patch(&mut self, patch: &Self::Patch);
}

不确定特质是否有帮助。 - Stargateur
1
嗯...我在想是否可以编写一个宏来推导出那个。不确定。但那应该可行。如果还没有人建议过,我也认为我会向serde建议类似的东西,因为对于任何实现幂等REST风格接口的人来说,这似乎是一个毫无头脑的功能。 - Adam Ierymenko
@Stargateur 如果你想将其实现为派生宏,这个特质会很有帮助 :) - Peter Hall
@Stargateur,它还创建了类型之间的一对一映射。 <T as Patch> :: Patch是一个具有参数 T的类型级函数,它返回T的“修补”类型。 - Peter Hall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接