您的问题是 hcl 默认情况下似乎可以解释
nested_block "nested_block_label" {
foo = 123
}
如以下“serde结构”:
"nested_block" -> {
"nested_block_label" -> {
"foo" -> 123
}
}
但是对于您的 Rust 结构,它必须是
"nested_block" -> {
"label" -> "nested_block_label"
"foo" -> 123
}
我不知道有任何attributes 可以让您将前者弯曲成后者。
像往常一样,当遇到这种情况时,通常最容易首先反序列化为hcl::Block 这样的通用结构,然后手动转换为您想要的任何结构。缺点是您必须分别为每个结构执行此操作。
理论上,您可以实现一个通用的反序列化函数,该函数将接收到的Deserializer 包装起来,并将您获得的两级结构展平为您想要的一级结构。但是实现反序列化器需要大量的样板文件。可能以前有人做过,但我不知道有任何板条箱可以在这里帮助你。
作为一种中等努力的解决方案,您可以拥有一个特殊的 Labelled 包装器结构,它始终可以捕获并展平这个中间级别:
#[derive(Debug, PartialEq)]
struct Labelled<T> {
label: String,
t: T,
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Labelled<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct V<T>(std::marker::PhantomData<T>);
impl<'de, T: Deserialize<'de>> serde::de::Visitor<'de> for V<T> {
type Value = Labelled<T>;
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
if let (Some((label, t)), None) =
(map.next_entry()?, map.next_entry::<String, ()>()?)
{
Ok(Labelled { label, t })
} else {
Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Other("Singleton map"),
&self,
))
}
}
}
deserializer.deserialize_map(V::<T>(Default::default()))
}
}
会这样使用:
#[derive(Deserialize, PartialEq, Debug)]
struct TestRoot {
nested_block: Labelled<TestNested>,
}
#[derive(Deserialize, PartialEq, Debug)]
struct TestNested {
foo: u32,
}
最后,可能会有一些技巧,比如添加#[serde(rename = "$hcl::label")]。其他序列化库(例如quick-xml)有类似的并允许以这种方式将字段标记为特殊的东西。 hcl-rs 在内部做同样的事情,但它没有记录,我无法从源头上弄清楚你需要什么是可能的。