3

我正在使用 YamlDotNet 来解析简单的配置文件(没有深度嵌套等)。反序列化器将解析包含重复字段的字符串,覆盖之前的值。例如,

foo: bar
foo: baz

被认为等同于

foo: baz

对于我的应用程序,我希望这样的重复会导致抛出异常。这可能吗?

4

3 回答 3

3

UniqueKeysDictionary示例对我不起作用,并且从第二个示例中不清楚如何准确验证。但是如果不允许任何重复并且两次加载文件是可以接受的,我发现了更简单的方法:

    private T DeserializeAndValidate<T>(StreamReader reader)
    {
        var yaml = new YamlStream();
        yaml.Load(reader); // throws if duplicates are found

        reader.BaseStream.Seek(0, SeekOrigin.Begin);
        using (var reader2 = new StreamReader(reader.BaseStream))
        {
            var deserializer = new Deserializer();
            var data = deserializer.Deserialize<T>(reader2);
            return data;
        }
    }
于 2018-11-23T11:00:33.820 回答
1

默认节点反序列化器使用索引器来分配值。实现所需行为的一种方法是反序列化为不允许重复值的类型,例如:

public class UniqueKeysDictionary<TKey, TValue>
    : Dictionary<TKey, TValue>
    , IDictionary<TKey, TValue>
{
    TValue IDictionary<TKey, TValue>.this[TKey key]
    {
        get { return base[key]; }
        set { base.Add(key, value); }
    }
}

可以在此处找到一个完整的工作示例

该解决方案的一个重要问题是它违反了indexer 的约定,其行为应该是覆盖该值。

另一种方法是将 的实现替换为GenericDictionaryNodeDeserializer使用该Add()方法而不是索引器的实现。这是另一个示例的相关部分,展示了如何替换节点解串器

var deserializer = new Deserializer();

var objectDeserializer = deserializer.NodeDeserializers
    .Select((d, i) => new {
        Deserializer = d as ObjectNodeDeserializer,
        Index = i
    })
    .First(d => d.Deserializer != null);

deserializer.NodeDeserializers[objectDeserializer.Index] =
    new ValidatingNodeDeserializer(objectDeserializer.Deserializer);
于 2015-06-02T12:58:03.557 回答
1

有一个涉及 linter 的解决方案,但我不确定它是否与您相关,因为它不会导致在 YamlDotNet 中引发异常。无论如何我都会发布它,因为它可以避免替换 GenericDictionaryNodeDeserializer.

这是yamlint命令行工具:

sudo pip install yamllint

具体来说,它有一个key-duplicates检测重复键的规则:

$ cat test.yml
foo: bar
foo: baz

$ yamllint test.yml
test.yml
  2:1       error    duplication of key "foo" in mapping  (key-duplicates)
于 2016-02-02T10:38:45.607 回答