如何反序列化 YamlDotNet 中的 YamlNode?
How to deserialize a YamlNode in 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);
}
}
一定有更好的方法,我只是还没有找到。
目前无法从 YamlNode
反序列化,您的方法是可能的解决方法之一。如果你想避免将节点写入缓冲区,你可以实现从 YamlNode
读取的 IParser
接口,如 this example.
我在上面的例子中的做法是创建一个适配器,将 YamlNode
转换为 IRnumerable<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();
}
}
然后您可以使用适配器从任何 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)
)
);
我有一个应用程序,我可以在其中读取我事先不知道其结构的任意 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);
}
}
一定有更好的方法,我只是还没有找到。
目前无法从 YamlNode
反序列化,您的方法是可能的解决方法之一。如果你想避免将节点写入缓冲区,你可以实现从 YamlNode
读取的 IParser
接口,如 this example.
我在上面的例子中的做法是创建一个适配器,将 YamlNode
转换为 IRnumerable<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();
}
}
然后您可以使用适配器从任何 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)
)
);