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"]]]]

我探索的一条捷径是利用 Newton 的 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 = ... }。您必须使用反射动态创建匿名类型。这很糟糕。文章“Extend Anonymous Types using 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 回答