如何在 YamlDotNet 中反序列化 YamlNode?

Dan*_*ker 5 c# serialization yaml yamldotnet

我有一个应用程序,可以读取任意 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Ant*_*bry 5

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

我在上面的示例中所做的方法是创建一个将 a 转换YamlNode为 an 的适配器IEnumerable<ParsingEvent>

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

一旦有了这个,创建一个适配器就很简单了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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用适配器从任何YamlStream,YamlDocument或 反序列化YamlNode

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)
    )
);
Run Code Online (Sandbox Code Playgroud)