深度嵌套动态结构的 YamlDotNet 反序列化

Phi*_*els 3 c# yamldotnet

我有一个深层嵌套的对象模型:

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

Phi*_*els 6

好吧,我在某种程度上回答了我自己的问题。这个课程将按照我对文档的要求进行操作(未在其他课程上进行测试)。每个 YAML 字符串可以轻松扩展到多个文档。可以通过尝试转换为等来改进标量的处理doubleDateTime

当然有更好的方法来做到这一点,但是这个项目的 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)

谨防!此代码尚未经过完全测试!可能存在一些边缘条件。