3

深くネストされたオブジェクト モデルがあります。

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; }
}

そして、これを問題なく YAML にシリアル化しました。

var serializer = new YamlDotNet.Serialization.Serializer();
var sb = new StringBuilder();
var sw = new StringWriter(sb);
serializer.Serialize(sw, model);
string yaml = sb.ToString();

yaml は見栄えがよく、JSON または HJSON 表現に非常に似ています。

私は今それをデシリアライズしたい - 元のモデルではなく動的オブジェクトにデシリアライズしたい (これはこの例で最初に YAML を生成するためにのみ使用されており、最終的なアセンブリには存在しません) .

var sr = new StringReader(yaml);
var deserializer = new YamlDotNet.Serialization.Deserializer();
dynamic expando = deserializer.Deserialize<ExpandoObject>(sr);

問題は、結果として得られる expando が非常に使いにくく、不要なレベルのネストが多数含まれていることです。例えば:

expando.Sections[0]["Title"]
expando.Sections[0]["SubSections"][0]["Title"]
expando.Sections[0]["SubSections"][0]["Cheats"][0]["Text"]

しかし、私はこれが欲しい

expando.Sections[0].Title
expando.Sections[0].SubSections[0].Title
expando.Sections[0].SubSections[0].Cheats[0].Text

これは何らかの方法で可能ですか?

コミット 2db9a0491e8ab50bb07aee552ddec6697c4b8bfc で、プロジェクト Gitcheatsheet.TestHarness のhttps://github.com/PhilipDaniels/Lithogenで利用可能な再現プログラムがあります。

4

3 に答える 3

4

さて、私は自分自身の質問にある程度答えました。このクラスは、私がドキュメントに対して要求したことを実行します (他のクラスではテストされていません)。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;
    }
}

SetProperty は拡張メソッドです。何らかの理由で思い出せません:

public static void SetProperty(this IDictionary<string, object> target, string name, object thing)
{
    target[name] = thing;
}

注意してください!このコードは完全にはテストされていません! おそらくいくつかのエッジ条件があります。

于 2015-02-25T20:18:15.017 に答える
-1

「(この例では最初に YAML を生成するためにのみ使用されており、最終的なアセンブリには存在しません)」と言うと、これを行う方法を効果的に排除しています。この元のモデルを含めることができない理由によっては、何らかの形で典型的な依存関係として参照できない場合は、最終的なアセンブリにコピーすることを検討することをお勧めします。

具体的には:

アクセスしたい場合

expando.Sections[0]["Title"]
expando.Sections[0]["SubSections"][0]["Title"]
expando.Sections[0]["SubSections"][0]["Cheats"][0]["Text"]

お気に入り

expando.Sections[0].Title
expando.Sections[0].SubSections[0].Title
expando.Sections[0].SubSections[0].Cheats[0].Text

次に、コンパイラがデータの構造を理解し、.Subsections[0]. したがって、このデータを逆シリアル化するアセンブリ (「最終アセンブリ」) にオブジェクト モデルを含めることができれば、次の方法で逆シリアル化できます。

var sr = new StringReader(yaml);
var deserializer = new YamlDotNet.Serialization.Deserializer();
Section[] sections = deserializer.Deserialize<Section[]>(sr);

でデータにアクセスする

sections[0].Title
sections[0].Subsections[0].Title

于 2017-10-05T04:05:08.263 に答える