我有一个看起来像这样的 struct :

#[derive(Serialize, Deserialize)]
pub struct TestItem {
    pub a: String,
    pub b: String,
}

#[derive(Serialize, Deserialize)]
pub struct Test {
    pub items: Vec<TestItem>,
}

我想使用InlineTable来序列化它,因为它的可读性更强,但toml crate 默认为常规表.例如我想要:

items = [
    { a = "testa1", b = "testb1" },
    # etc
]

toml::to_string(...)

[[items]]
a = "testa1"
b = "testb1"

#etc

这似乎是一个相当常见的请求,例如请参阅toml#592,并且有一个example in the toml_edit crate在这种情况下似乎不起作用,因为itemsArrayOfTables.

我try 过以下访客


impl VisitMut for TestVisitor {
    fn visit_table_mut(&mut self, node: &mut Table) {
        visit_table_mut(self, node);
        node.set_implicit(true);
    }

    fn visit_table_like_kv_mut(&mut self, mut key: KeyMut<'_>, node: &mut Item) {
        println!("This is hit: {:?} -> {node:?}", key.get());
        if let Item::Table(table) = node {
            println!("This is never hit, as its an ArrayOfTables");
            let table = std::mem::replace(table, Table::new());
            let inline_table = table.into_inline_table();
            key.fmt();
            *node = Item::Value(Value::InlineTable(inline_table));
        }

        // Recurse further into the document tree.
        visit_table_like_kv_mut(self, key, node);
    }

    fn visit_array_mut(&mut self, node: &mut Array) {
        node.fmt();
    }
}

但这似乎跳过了ArrayOfTables中的Table,所以if永远不会被击中.输出是:

This is hit: "items" -> ArrayOfTables(...)
This is hit: "a" -> Value(String(...)
// etc - the tables are never visited

我该如何构建一个可以将items内的任何表转换为InlineTable的访问者?似乎VisitMut中的所有其他方法都采用Table,所以我无法按照示例要求将它们Mutations 为InlineTable.

推荐答案

注:Chayim Friedman在上面的 comments 中指出,toml_edit::ser::to_string()将所有表格打印为InlineTable,这与toml::to_stringtoml::to_string_prettytoml_edit::to_string_pretty的行为不同.我的用例稍微复杂一些,因为我只想以内联形式打印some个表,所以我创建了一个自定义访问者,如下所述.


好吧,看看这两种不同的TOML格式实际上以不同的方式解析.

[[items]]
a = "testa1"
b = "testb1"

解析为Item::ArrayOfTables,仅接受Table作为其项目,而:

items = [
    { a = "testa1", b = "testb1" },
    # etc
]

解析为Item::Value(Value::Array(...)),其中元素为Value::InlineTable.这意味着像下面这样的访客似乎可以做到这一点.

struct TestVisitor;

// I could be wrong here, but it seems I have to use the `kv` variant here
// because `visit_table_like_mut` has `node: &mut dyn toml_edit::TableLike`
// as the second argument, which `ArrayOfTables` doesn't seem to impl
impl VisitMut for TestVisitor {
    fn visit_table_like_kv_mut(&mut self, mut key: KeyMut<'_>, node: &mut Item) {
        if let Item::ArrayOfTables(tables) = node {
            let new_tables = tables
                .iter_mut()
                .map(|t| {
                    let table = std::mem::replace(t, Table::new());
                    Value::InlineTable(table.into_inline_table()).decorated("\n", "")
                })
                .collect::<Vec<_>>();

            key.fmt();
            *node = Item::Value(Value::Array(Array::from_iter(new_tables.iter())));
        }

        // Recurse further into the document tree.
        visit_table_like_kv_mut(self, key, node);
    }
}

我可以这样使用:

impl Test {
    pub fn to_toml(&self) -> anyhow::Result<String> {
        let mut doc = toml_edit::ser::to_document(self)?;

        let mut visitor = TestVisitor;
        visitor.visit_document_mut(&mut doc);

        Ok(doc.to_string())
    }
}

Rust相关问答推荐

为什么父作用域中的变量超出了子作用域

亚性状上位性状上的 rust 病伴生型界限

在Rust中显式装箱受生存期限制的转换闭包

支持TLS的模拟HTTP服务器

原始数组数据类型的默认trait实现

为什么Deref类特征不构成?

如果变量本身不是None,如何返回;如果没有,则返回None&Quot;?

使用 serde::from_value 反序列化为泛型类型

如何将 struct 数组放置在另一个 struct 的末尾而不进行内存分段

Rust 中多个 & 符号的内存表示

方法可以被误认为是标准特性方法

在发布中包含 Rust DLL

为什么 File::read_to_end 缓冲区容量越大越慢?

判断 is_ok 后重用结果

为什么 &i32 可以与 Rust 中的 &&i32 进行比较?

为什么 Rust 编译器在移动不可变值时执行复制?

我可以在不调用 .clone() 的情况下在类型转换期间重用 struct 字段吗?

HashMap entry() 方法使borrow 的时间比预期的长

C++ 中的 CRTP 是一种表达其他语言中特征和/或 ADT 的方法吗?

Rust:为什么在 struct 中borrow 引用会borrow 整个 struct?