1

私は LightWeight JSON Spec JsonRの C# の側面を実装しようとしていますが、どのような種類の再帰についても理解できません:-/ 誰かがここで助けてくれれば、それは大歓迎です。

// Mockup class
public class User {
    public string Name { get; set; }    
    public int Age     { get; set; }
    public List<string> Photos  { get; set; }
    public List<Friend> Friends { get; set; }       
}

// Mockup class
public class Friend {
    public string FirstName { get; set; }
    public string LastName  { get; set; }
}

// Initialize objects
var userList = new List<User>();
userList.Add(new User() { 
    Name   = "Robert",
    Age     = 32,
    Photos  = new List<string> { "1.jpg", "2.jpg" },
    Friends = new List<Friend>() {
        new Friend() { FirstName = "Bob", LastName = "Hope"},
        new Friend() { FirstName = "Mr" , LastName = "T"}
    }
}); 
userList.Add(new User() { 
    Name   = "Jane",
    Age     = 21,
    Photos  = new List<string> { "4.jpg", "5.jpg" },
    Friends = new List<Friend>() {
        new Friend() { FirstName = "Foo"  , LastName = "Bar"},
        new Friend() { FirstName = "Lady" , LastName = "Gaga"}
    }
});

すべての背後にある考え方は、上記のオブジェクトを取得して、キーを含むコレクションと値を含むコレクションの 2 つの個別のコレクションに分割することです。このように、最終的にはワイヤ経由でのみ値を送信できるため、多くの帯域幅を節約し、クライアントで再結合できます (再結合のための js 実装は既に存在します)。

すべてがうまくいけば、上記のオブジェクトからこれを取得できるはずです

var keys   = new object[] {
    "Name", "Age", "Photos",
    new { Friends = new [] {"FirstName", "LastName"}}};
var values = new [] {
    new object[] {"Robert", 32, new [] {"1.jpg", "2.jpg"},
                                new [] { new [] {"Bob", "Hope"},
                                         new [] {"Mr", "T"}}},
    new object[] {"Jane", 21, new [] {"4.jpg", "5.jpg"},
                              new [] { new [] {"Foo", "Bar"},
                                       new [] {"Lady", "Gaga"}}}};

検証として、結果の適合性をテストできます

Newtonsoft.Json.JsonConvert.SerializeObject(keys).Dump("keys");
// Generates:
// ["Name","Age","Photos",{"Friends":["FirstName","LastName"]}]

Newtonsoft.Json.JsonConvert.SerializeObject(values).Dump("values");
// Generates:
// [["Robert",32,["1.jpg","2.jpg"],[["Bob","Hope"],["Mr","T"]]],["Jane",21,["4.jpg","5.jpg"],[["Foo","Bar"],["Lady","Gaga"]]]]

私が調べたショートカットは、このようなニュートンの JArray/JObject 機能を利用することでした

var JResult = Newtonsoft.Json.JsonConvert.DeserializeObject(
    Newtonsoft.Json.JsonConvert.SerializeObject(userList));

このように、すでに反復を開始できる一種の配列オブジェクトになります

メモリ/速度効率の良い方法でこれをクラックできると思う人はいますか?

4

2 に答える 2

2

サンプルデータで機能するソリューションがあります。これは普遍的な解決策ではなく、他の例では失敗する可能性がありますが、再帰の使用方法を示しています。エラー処理は含まれていません。実際のソリューションはそうしなければならないでしょう。

ジェネリックリストのアイテムタイプを取得するこのヘルパーメソッドを使用します。

private static Type GetListItemType(Type listType)
{
    Type itemType = null;
    foreach (Type interfaceType in listType.GetInterfaces()) {
        if (interfaceType.IsGenericType &&
            interfaceType.GetGenericTypeDefinition() == typeof(IList<>)) {
            itemType = interfaceType.GetGenericArguments()[0];
            break;
        }
    }
    return itemType;
}

さて、再帰:

public void SplitKeyValues(IList source, List<object> keys, List<object> values)
{
    Type itemType = GetListItemType(source.GetType());
    PropertyInfo[] properties = itemType.GetProperties();
    for (int i = 0; i < source.Count; i++) {
        object item = source[i];
        var itemValues = new List<object>();
        values.Add(itemValues);
        foreach (PropertyInfo prop in properties) {
            if (typeof(IList).IsAssignableFrom(prop.PropertyType) &&
                prop.PropertyType.IsGenericType) {
                // We have a List<T> or array

                Type genericArgType = GetListItemType(prop.PropertyType);
                if (genericArgType.IsValueType || genericArgType == typeof(string)) {
                    // We have a list or array of a simple type
                    if (i == 0)
                        keys.Add(prop.Name);
                    List<object> subValues = new List<object>();
                    itemValues.Add(subValues);
                    subValues.AddRange(
                        Enumerable.Cast<object>(
                            (IEnumerable)prop.GetValue(item, null)));
                } else {
                    // We have a list or array of a complex type
                    List<object> subKeys = new List<object>();
                    if (i == 0)
                        keys.Add(subKeys);
                    List<object> subValues = new List<object>();
                    itemValues.Add(subValues);
                    SplitKeyValues(
                        (IList)prop.GetValue(item, null), subKeys, subValues);
                }
            } else if (prop.PropertyType.IsValueType ||
                       prop.PropertyType == typeof(string)) {
                // We have a simple type
                if (i == 0)
                    keys.Add(prop.Name);
                itemValues.Add(prop.GetValue(item, null));
            } else {
                // We have a complex type.
                // Does not occur in your example
            }
        }
    }
}

私はそれをこのように呼びます:

List<User> userList = InitializeObjects();
List<object> keys = new List<object>();
List<object> values = new List<object>();
SplitKeyValues(userList, keys, values);

InitializeObjects上記のようにユーザーリストを初期化します。


アップデート

問題は、匿名タイプを使用していることですnew { Friends = ... }。リフレクションを使用して、匿名タイプを動的に作成する必要があります。そして、それはかなり厄介です。記事「Reflection.Emitを使用して匿名型を拡張する」はそれを行っているようです。(私はそれをテストしませんでした)。

たぶん、もっと簡単なアプローチでうまくいくでしょう。クラスタイプの説明のためにヘルパークラスを作成することをお勧めします。

public class Class
{
    public string Name { get; set; }
    public List<object> Structure { get; set; }
}

次に、上記のコードのelseケースを置き換えましょう。

...
} else {
    // We have a list or array of a complex type
    List<object> subKeys = new List<object>();
    var classDescr = new Class { Name = genericArgType.Name, Structure = subKeys };
    if (i == 0)
        keys.Add(classDescr);
    List<object> subValues = new List<object>();
    itemValues.Add(subValues);
    SplitKeyValues(
        (IList)prop.GetValue(item, null), subKeys, subValues);
}
...

結果は次のとおりです。 ここに画像の説明を入力してください

于 2012-12-15T18:22:11.487 に答える
0

規約ベースのマッピング ライブラリであるAutoMapperなどの外部ツールを試してみることをお勧めします。

独自の規則でフラット化機能を試すことをお勧めします。

時間がなくて例を書き出せなくて申し訳ありませんが、このオープン ソース ライブラリの背後にあるアイデアは大いに役立つと思います。

于 2012-12-15T16:10:00.907 に答える