用标签反序列化hcl



我正在尝试使用hcl-rs = 0.7.0来解析一些HCL。我只是在试验任意的HCL,所以我不想解析地形特定的代码。

我希望能够解析这样的块,并将其标签作为结果的一部分

nested_block "nested_block_label" {
foo = 123
}

这目前不起作用,但希望它能表明我的意图。这样的事情可能发生吗?

#[test]
fn deserialize_struct_with_label() {
#[derive(Deserialize, PartialEq, Debug)]
struct TestRoot {
nested_block: TestNested,
}
#[derive(Deserialize, PartialEq, Debug)]
struct TestNested {
label: String,
foo: u32,
}

let input = r#"
nested_block "nested_block_label" {
foo = 123
}"#;
let expected = TestRoot{ nested_block: TestNested { label: String::from("nested_block_label"), foo: 123 } };
assert_eq!(expected, from_str::<TestRoot>(input).unwrap());
}

您的问题是默认情况下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
}

我不知道有什么属性可以让你把前者变成后者。

和往常一样,当遇到这种情况时,通常最容易的方法是首先反序列化为像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在内部也做同样的事情,但它没有文档,我无法从源头上弄清楚你需要的东西是否可能。

最新更新