至少一个可选字段
At least one of the optional fields
我有一些结构,其中所有字段都是可选的,但至少必须指定其中一个或至少它们的特定组合。例如:
#[derive(Deserialize)]
struct test {
#[serde(default)]
a: String,
#[serde(default)]
b: String,
#[serde(default)]
c: String,
}
所有字段都是可选的,但至少应提供 b
或 c
之一。知道字段数可能更大,推荐的表示这种逻辑的方法是什么?
作为一个 medium-performance,中等强度的解决方案(与定制的解串器相比),我会
- 将反序列化重定向到
HashMap<String, String>
#[derive(Deserialize, Debug)]
#[serde(try_from = "TestDes")]
struct Test {
a: String,
b: String,
c: String,
}
#[derive(Deserialize)]
#[serde(transparent)]
struct TestDes(HashMap<String, String>);
- 告诉serde如何将
TestDes
转换成Test
impl TryFrom<TestDes> for Test {
type Error = &'static str;
fn try_from(mut value: TestDes) -> Result<Self, Self::Error> {
if /* check whether the combination of keys you need is present */ {
return Err("need at least one value in Test");
}
Ok(/*
extract value.0 into Test here
requires to repeat
x: value.0.delete("x").unwrap_or("".into()),
for each field
*/)
}
}
- 如果你想要更好的性能,你可以有一个包含
Test
的所有字段的 struct TestDes
作为 Option<String>
- 如果你不想重复
Test
的字段名称,你可以生成 Test
和 TryInto
实现(和 TestDes
,如果你切换Option<String>
变体)在单个宏调用中。
您写道:
All fields are optional but at least one of b or c should be provided.
阅读标题时,我的第一个想法是您希望 所有 个可选字段中至少有一个为空。但由于它只是 b
或 c
,这就是我的想法...
有时尝试获取库(serde
在这种情况下)为您做一些事情可能会使事情变得复杂得多,而不是值得的。我很可能只是直接检查反序列化实例的代码。这是 anyhow
's macro expect
:
的示例
let t: Test = ...; // Get a deserialized instance
ensure!(!t.b.is_empty() || !t.c.is_empty(), "Either b or c needs to be provided!");
我并不是说这应该始终是方法,但在许多情况下,我认为这是最明智(和简单)的做法。通常当我反序列化来自 JSON(或其他一些数据格式)的数据并且我需要验证该数据时,无论如何我都必须实施一些 application-specific 验证 - 不能总是以声明方式表达的东西。通常,验证会去一个地方(比如说一个函数)。我通常依靠库来 运行 至少为我做一些验证(比如必填字段、字段类型等),但有时,即使库可以配置为进行更高级的验证,它根本不值得,或者最终结果不会那么好(例如,我可能想提供一个 more-meaningful 错误消息而不是默认消息/以防库没有为我提供这样的选项/).
我有一些结构,其中所有字段都是可选的,但至少必须指定其中一个或至少它们的特定组合。例如:
#[derive(Deserialize)]
struct test {
#[serde(default)]
a: String,
#[serde(default)]
b: String,
#[serde(default)]
c: String,
}
所有字段都是可选的,但至少应提供 b
或 c
之一。知道字段数可能更大,推荐的表示这种逻辑的方法是什么?
作为一个 medium-performance,中等强度的解决方案(与定制的解串器相比),我会
- 将反序列化重定向到
HashMap<String, String>
#[derive(Deserialize, Debug)]
#[serde(try_from = "TestDes")]
struct Test {
a: String,
b: String,
c: String,
}
#[derive(Deserialize)]
#[serde(transparent)]
struct TestDes(HashMap<String, String>);
- 告诉serde如何将
TestDes
转换成Test
impl TryFrom<TestDes> for Test {
type Error = &'static str;
fn try_from(mut value: TestDes) -> Result<Self, Self::Error> {
if /* check whether the combination of keys you need is present */ {
return Err("need at least one value in Test");
}
Ok(/*
extract value.0 into Test here
requires to repeat
x: value.0.delete("x").unwrap_or("".into()),
for each field
*/)
}
}
- 如果你想要更好的性能,你可以有一个包含
Test
的所有字段的struct TestDes
作为Option<String>
- 如果你不想重复
Test
的字段名称,你可以生成Test
和TryInto
实现(和TestDes
,如果你切换Option<String>
变体)在单个宏调用中。
您写道:
All fields are optional but at least one of b or c should be provided.
阅读标题时,我的第一个想法是您希望 所有 个可选字段中至少有一个为空。但由于它只是 b
或 c
,这就是我的想法...
有时尝试获取库(serde
在这种情况下)为您做一些事情可能会使事情变得复杂得多,而不是值得的。我很可能只是直接检查反序列化实例的代码。这是 anyhow
's macro expect
:
let t: Test = ...; // Get a deserialized instance
ensure!(!t.b.is_empty() || !t.c.is_empty(), "Either b or c needs to be provided!");
我并不是说这应该始终是方法,但在许多情况下,我认为这是最明智(和简单)的做法。通常当我反序列化来自 JSON(或其他一些数据格式)的数据并且我需要验证该数据时,无论如何我都必须实施一些 application-specific 验证 - 不能总是以声明方式表达的东西。通常,验证会去一个地方(比如说一个函数)。我通常依靠库来 运行 至少为我做一些验证(比如必填字段、字段类型等),但有时,即使库可以配置为进行更高级的验证,它根本不值得,或者最终结果不会那么好(例如,我可能想提供一个 more-meaningful 错误消息而不是默认消息/以防库没有为我提供这样的选项/).