【问题标题】:How to deserialize a YamlNode in YamlDotNet?如何反序列化 YamlDotNet 中的 YamlNode?
【发布时间】:2016-11-19 18:46:01
【问题描述】:

我有一个应用程序,我可以在其中读取我事先不知道其结构的任意 Yaml 文件。我发现 YamlStream 和其他 YamlNode 实现很有用,因为它们允许我遍历整个 Yaml 文件。但是,有时我有一个YamlNode,通常是YamlScalarNode,我想使用YamlDotNet 的能力将该节点反序列化为一个对象。我该怎么做?

这是我尝试过的。它很难看,而且它只适用于带有显式标签的节点(例如,!!bool "true" 变为 true,但 1 变为 "1"):

private T DeserializeNode<T>(YamlNode node)
{
    if (node == null)
        return default(T);

    using (var stream = new MemoryStream())
    using (var writer = new StreamWriter(stream))
    using (var reader = new StreamReader(stream))
    {
        new YamlStream(new YamlDocument[] { new YamlDocument(node) }).Save(writer);
        writer.Flush();
        stream.Position = 0;
        return new Deserializer().Deserialize<T>(reader);
    }
}

一定有更好的方法,我还没有找到。

【问题讨论】:

    标签: c# serialization yaml yamldotnet


    【解决方案1】:

    目前无法从YamlNode 反序列化,您的方法是可能的解决方法之一。如果要避免将节点写入缓冲区,可以实现从YamlNode 读取的IParser 接口,例如this example

    我在上面示例中的做法是创建一个适配器,将YamlNode 转换为IRnumerable&lt;ParsingEvent&gt;

    public static class YamlNodeToEventStreamConverter
    {
        public static IEnumerable<ParsingEvent> ConvertToEventStream(YamlStream stream)
        {
            yield return new StreamStart();
            foreach (var document in stream.Documents)
            {
                foreach (var evt in ConvertToEventStream(document))
                {
                    yield return evt;
                }
            }
            yield return new StreamEnd();
        }
    
        public static IEnumerable<ParsingEvent> ConvertToEventStream(YamlDocument document)
        {
            yield return new DocumentStart();
            foreach (var evt in ConvertToEventStream(document.RootNode))
            {
                yield return evt;
            }
            yield return new DocumentEnd(false);
        }
    
        public static IEnumerable<ParsingEvent> ConvertToEventStream(YamlNode node)
        {
            var scalar = node as YamlScalarNode;
            if (scalar != null)
            {
                return ConvertToEventStream(scalar);
            }
    
            var sequence = node as YamlSequenceNode;
            if (sequence != null)
            {
                return ConvertToEventStream(sequence);
            }
    
            var mapping = node as YamlMappingNode;
            if (mapping != null)
            {
                return ConvertToEventStream(mapping);
            }
    
            throw new NotSupportedException(string.Format("Unsupported node type: {0}", node.GetType().Name));
        }
    
        private static IEnumerable<ParsingEvent> ConvertToEventStream(YamlScalarNode scalar)
        {
            yield return new Scalar(scalar.Anchor, scalar.Tag, scalar.Value, scalar.Style, false, false);
        }
    
        private static IEnumerable<ParsingEvent> ConvertToEventStream(YamlSequenceNode sequence)
        {
            yield return new SequenceStart(sequence.Anchor, sequence.Tag, false, sequence.Style);
            foreach (var node in sequence.Children)
            {
                foreach (var evt in ConvertToEventStream(node))
                {
                    yield return evt;
                }
            }
            yield return new SequenceEnd();
        }
    
        private static IEnumerable<ParsingEvent> ConvertToEventStream(YamlMappingNode mapping)
        {
            yield return new MappingStart(mapping.Anchor, mapping.Tag, false, mapping.Style);
            foreach (var pair in mapping.Children)
            {
                foreach (var evt in ConvertToEventStream(pair.Key))
                {
                    yield return evt;
                }
                foreach (var evt in ConvertToEventStream(pair.Value))
                {
                    yield return evt;
                }
            }
            yield return new MappingEnd();
        }
    }
    

    一旦你有了这个,为IParser创建一个适配器就很简单了,因为这两个接口基本上是等价的:

    public class EventStreamParserAdapter : IParser
    {
        private readonly IEnumerator<ParsingEvent> enumerator;
    
        public EventStreamParserAdapter(IEnumerable<ParsingEvent> events)
        {
            enumerator = events.GetEnumerator();
        }
    
        public ParsingEvent Current
        {
            get
            {
                return enumerator.Current;
            }
        }
    
        public bool MoveNext()
        {
            return enumerator.MoveNext();
        }
    }
    

    然后您可以使用适配器从任何YamlStreamYamlDocumentYamlNode 反序列化:

    var stream = new YamlStream();
    stream.Load(new StringReader(input));
    
    var deserializer = new DeserializerBuilder()
        .WithNamingConvention(new CamelCaseNamingConvention())
        .Build();
    
    var prefs = deserializer.Deserialize<YOUR_TYPE>(
        new EventStreamParserAdapter(
            YamlNodeToEventStreamConverter.ConvertToEventStream(stream)
        )
    );
    

    【讨论】:

    • 这很棒而且非常完整。奇迹般有效!感谢您花时间编写它。
    • 太棒了 - 这个答案让我能够解决一个问题,即当别名试图在文档中稍后引用它们时,似乎找不到由 Deserializer 反序列化的锚点。注意这个问题本身只是因为github.com/aaubry/YamlDotNet/issues/295 导致我需要映射我的别名属性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多