首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用标签反序列化hcl

用标签反序列化hcl
EN

Stack Overflow用户
提问于 2022-09-09 20:35:35
回答 1查看 53关注 0票数 0

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

我希望能够解析这样的块并获得它的标签作为结果的一部分

代码语言:javascript
复制
nested_block "nested_block_label" {
    foo = 123
}

这目前不起作用,但希望它显示了我的意图。这样的事有可能吗?

代码语言:javascript
复制
#[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());
}
EN

回答 1

Stack Overflow用户

发布于 2022-09-10 05:09:39

你的问题是hcl在默认情况下似乎解释

代码语言:javascript
复制
nested_block "nested_block_label" {
    foo = 123
}

作为以下"serde结构“:

代码语言:javascript
复制
"nested_block" -> {
  "nested_block_label" -> {
    "foo" -> 123
  }
}

如果不是你的铁锈结构,它必须是

代码语言:javascript
复制
"nested_block" -> {
  "label" -> "nested_block_label"
  "foo" -> 123
}

我不知道有任何属性允许您将前者弯曲为后者。

与往常一样,当遇到这种情况时,通常最容易的方法是首先反序列化为泛型结构(如hcl::Block ),然后手动转换为任何您想要的结构。不利之处在于,您必须对每个结构分别执行此操作。

理论上,您可以实现一个泛型反序列化函数,它封装它接收到的Deserializer,并使您进入所需的一级结构的两级结构变平。但是实现反序列化需要大量的样板。也许,以前有人这样做过,但我也不知道有什么箱子能帮到你。

作为一种中等努力的解决方案,您可以有一个特殊的Labelled包装结构,它总是捕获和平放这个中间级别:

代码语言:javascript
复制
#[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()))
    }
}

会被这样使用:

代码语言:javascript
复制
#[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在内部也这样做,但是它是没有文档的,我无法从源代码中判断您所需要的是否可能。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73667260

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档