是否可以禁止 YAML 文件中的重复项?

Is it possible to disallow duplicates in YAML file?

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

foo: bar
foo: baz

被认为等同于

foo: baz

对于我的应用程序,我希望此类重复项会引发异常。这可能吗?

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

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); }
    }
}

A fully working example can be found here.

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

另一种方法是替换 GenericDictionaryNodeDeserializer with one that uses the Add() method instead of the indexer. This is the relevant portion of a different example that shows how to replace a node deserializer:

的实现
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);

有一个涉及 linter 的解决方案,但我不确定它是否与 你因为它不会导致在 YamlDotNet 中抛出异常。我会post它 无论如何,因为它可以避免替换的实现 GenericDictionaryNodeDeserializer.

这是yamllint命令行工具:

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)

UniqueKeysDictionary 示例对我不起作用,从第二个示例来看,不清楚如何验证。但是我发现如果不允许 any 重复并且可以接受加载文件两次:

    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;
        }
    }