我有一个深层嵌套的对象模型:
public class CheatSheet {
public string Leader { get; set; }
public List<Section> Sections { get; set; }
}
public class Section {
public string Title { get; set; }
public List<SubSection> SubSections { get; set; }
}
public class SubSection {
public string Title { get; set; }
public List<Cheat> Cheats { get; set; }
}
public class Cheat {
public string Affected { get; set; }
public string Text { get; set; }
public string Hint { get; set; }
public string Url { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我已将其序列化为 YAML,没有任何问题:
var serializer = new YamlDotNet.Serialization.Serializer();
var sb = new StringBuilder();
var sw = new StringWriter(sb);
serializer.Serialize(sw, model);
string yaml = sb.ToString();
Run Code Online (Sandbox Code Playgroud)
yaml 看起来不错,与 JSON 或 HJSON 表示非常相似。
我现在想要反序列化它 - 注意,我想将它反序列化为动态对象,而不是原始模型(仅在本示例中用于首先生成 YAML,它不会存在于最终程序集中) 。
var sr = new StringReader(yaml);
var deserializer = new YamlDotNet.Serialization.Deserializer();
dynamic expando = deserializer.Deserialize<ExpandoObject>(sr);
Run Code Online (Sandbox Code Playgroud)
问题是生成的 Expando 非常难以使用,包含许多不必要的嵌套级别。例如:
expando.Sections[0]["Title"]
expando.Sections[0]["SubSections"][0]["Title"]
expando.Sections[0]["SubSections"][0]["Cheats"][0]["Text"]
Run Code Online (Sandbox Code Playgroud)
但我希望这是
expando.Sections[0].Title
expando.Sections[0].SubSections[0].Title
expando.Sections[0].SubSections[0].Cheats[0].Text
Run Code Online (Sandbox Code Playgroud)
这有可能吗?
在https://github.com/PhilipDaniels/Lithogen项目 Gitcheatsheet.TestHarness 中有一个重现程序 ,提交 2db9a0491e8ab50bb07aee552ddec6697c4b8bfc
好吧,我在某种程度上回答了我自己的问题。这个课程将按照我对文档的要求进行操作(未在其他课程上进行测试)。每个 YAML 字符串可以轻松扩展到多个文档。可以通过尝试转换为等来改进标量的处理double。DateTime
当然有更好的方法来做到这一点,但是这个项目的 API 非常混乱。
public static class YamlUtils
{
/// <summary>
/// Converts a YAML string to an <code>ExpandoObject</code>.
/// </summary>
/// <param name="yaml">The YAML string to convert.</param>
/// <returns>Converted object.</returns>
public static ExpandoObject ToExpando(string yaml)
{
using (var sr = new StringReader(yaml))
{
var stream = new YamlStream();
stream.Load(sr);
var firstDocument = stream.Documents[0].RootNode;
dynamic exp = ToExpando(firstDocument);
return exp;
}
}
/// <summary>
/// Converts a YAML node to an <code>ExpandoObject</code>.
/// </summary>
/// <param name="node">The node to convert.</param>
/// <returns>Converted object.</returns>
public static ExpandoObject ToExpando(YamlNode node)
{
ExpandoObject exp = new ExpandoObject();
exp = (ExpandoObject)ToExpandoImpl(exp, node);
return exp;
}
static object ToExpandoImpl(ExpandoObject exp, YamlNode node)
{
YamlScalarNode scalar = node as YamlScalarNode;
YamlMappingNode mapping = node as YamlMappingNode;
YamlSequenceNode sequence = node as YamlSequenceNode;
if (scalar != null)
{
// TODO: Try converting to double, DateTime and return that.
string val = scalar.Value;
return val;
}
else if (mapping != null)
{
foreach (KeyValuePair<YamlNode, YamlNode> child in mapping.Children)
{
YamlScalarNode keyNode = (YamlScalarNode)child.Key;
string keyName = keyNode.Value;
object val = ToExpandoImpl(exp, child.Value);
exp.SetProperty(keyName, val);
}
}
else if (sequence != null)
{
var childNodes = new List<object>();
foreach (YamlNode child in sequence.Children)
{
var childExp = new ExpandoObject();
object childVal = ToExpandoImpl(childExp, child);
childNodes.Add(childVal);
}
return childNodes;
}
return exp;
}
}
Run Code Online (Sandbox Code Playgroud)
其中 SetProperty 是一个扩展方法,由于某种原因我不记得了:
public static void SetProperty(this IDictionary<string, object> target, string name, object thing)
{
target[name] = thing;
}
Run Code Online (Sandbox Code Playgroud)
谨防!此代码尚未经过完全测试!可能存在一些边缘条件。