2016-11-19 165 views
1

我有一个应用程序,我可以阅读任何Yaml文件,其结构我事先不知道。我发现YamlStream和其他YamlNode实现有用,因为它们允许我遍历整个Yaml文件。然而,在某些时候,我有一个YamlNode,通常是YamlScalarNode,我想用YamlDotNet的能力将该节点反序列化为一个对象。我怎样才能做到这一点?如何反序列化YamlDotNet中的YamlNode?

这是我试过的。这是丑陋的,它仅适用于有明确的标签节点(例如!!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); 
    } 
} 

必须有,我只是还没有找到更好的办法。

回答

2

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

我做到了,在上面的例子中的方法是创建一种转换适配器YamlNodeIRnumerable<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(); 
    } 
} 

一旦你有了这个,实在是小巫见大巫为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) 
    ) 
); 
+0

这是伟大的,非常完整。奇迹般有效!感谢您花时间写下来。 – Virtlink