2016-11-19 170 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