【问题标题】:Can I read YamlDotNet mappings sequentially?我可以按顺序读取 YamlDotNet 映射吗?
【发布时间】:2015-05-01 09:31:10
【问题描述】:

是否可以按照它们在源文档中出现的顺序访问映射的键? IE。如果我有这个简单的文件:

values:
    first: something1
    second: something2
    third: something3

然后我就可以按原始顺序获得一系列键:[第一,第二,第三]?

【问题讨论】:

    标签: .net yaml yamldotnet


    【解决方案1】:

    实现此目的的一种方法是使用RepresentationModel API。它允许获得与底层结构紧密匹配的 YAML 文档的表示:

    var stream = new YamlStream();
    stream.Load(new StringReader(yaml));
    
    var document = stream.Documents.First();
    
    var rootMapping = (YamlMappingNode)document.RootNode;
    var valuesMapping = (YamlMappingNode)rootMapping.Children[new YamlScalarNode("values")];
    
    foreach(var tuple in valuesMapping.Children)
    {
        Console.WriteLine("{0} => {1}", tuple.Key, tuple.Value);
    }
    

    这种方法的缺点是您需要“手动”解析文档。另一种方法是使用序列化,并使用保留顺序的类型。我不知道有任何现成的IDictionary<TKey, TValue> 实现具有此特性,但如果您不关心高性能,实现起来相当简单:

    // NB: This is a minimal implementation that is intended for demonstration purposes.
    //     Most of the methods are not implemented, and the ones that are are not efficient.
    public class OrderPreservingDictionary<TKey, TValue>
        : List<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>
    {
        public void Add(TKey key, TValue value)
        {
            Add(new KeyValuePair<TKey, TValue>(key, value));
        }
    
        public bool ContainsKey(TKey key)
        {
            throw new NotImplementedException();
        }
    
        public ICollection<TKey> Keys
        {
            get { throw new NotImplementedException(); }
        }
    
        public bool Remove(TKey key)
        {
            throw new NotImplementedException();
        }
    
        public bool TryGetValue(TKey key, out TValue value)
        {
            throw new NotImplementedException();
        }
    
        public ICollection<TValue> Values
        {
            get { throw new NotImplementedException(); }
        }
    
        public TValue this[TKey key]
        {
            get
            {
                return this.First(e => e.Key.Equals(key)).Value;
            }
            set
            {
                Add(key, value);
            }
        }
    }
    

    一旦你有了这样的容器,你就可以利用Serialization API 来解析文档:

    var deserializer = new Deserializer();
    var result = deserializer.Deserialize<Dictionary<string, OrderPreservingDictionary<string, string>>>(new StringReader(yaml));
    
    foreach(var tuple in result["values"])
    {
        Console.WriteLine("{0} => {1}", tuple.Key, tuple.Value);
    }
    

    You can see a fully working example in this fiddle

    【讨论】:

    • 只是为了那些想要实现有序字典的人:我认为 this[TKey key] 实现错误,你应该首先检查 key 是否存在,除非你想要重复。另见this
    • 我相信这里的 RepresentationModel 示例并不能保证键是按顺序读取的,因为至少在我看到的 Yaml.NET 代码中,Children 成员此时已经是一个 Dictionary。 Dictionary 不保证按照插入的顺序迭代键。 (旁注:在这种情况下,它碰巧按照它们今天插入的顺序返回它们,但文档说顺序是未定义的,我们不应该依赖它)。我发现我可以为我的类型实现一个 IYamlTypeConverter 并使用 IParser 手动读取地图。不理想,但有效。
    • @AmberScouras 你是对的。 RepresentationModel 使用标准Dictionary&lt;,&gt;,它不会保留顺序。我已经打开了issue 来记住要修复它。感谢您指出。
    猜你喜欢
    • 2012-02-12
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-30
    • 1970-01-01
    • 2019-11-21
    相关资源
    最近更新 更多