不同自定义 类 和集合的反射 GetProperties

Reflection GetProperties on different custom classes and collections

在 Web API 中,我创建了一个自定义 MediaTypeFormatter 来创建具有特定模式的 JSON 输出,这使得标准序列化不合适。并不是说上面的上下文可能相关,但我需要使用我指定的模式将对象和 IEnumerable 转换为 JSON。

在伪代码中:

If object is a collection
  foreach item in collection
     Write the name/type of the item (will be same for all)
         foreach property of item
              Write the property name, type, and value
Else
  foreach property of object 
     Write the property name, type, and value

我最感兴趣的部分是通过反射获得class属性name/value。

例如,这是从控制器发送的:

return new MyPerson { .FirstName = "Bob", .DateOfBirth = Convert.ToDateTime("1979-03-01") }

...将输出为(粗略示例,因为 JSON 很容易更改以制作必要的模式):

{ "MyPerson" : {"FirstName": {"value": "Bob", "Type": "string"}}, "DateOfBirth": {"value": "1979-03-01", "Type": "date"}}

同样,将迭代一个集合以产生类似的输出:

return new IEnumerable<Foo>() {
    new Foo() { Param1 = "aaa", Param2 = "bbb" },
    new Foo() { Param1 = "ccc", Param2 = "ddd" }
}

...生产

{ "FooCollection": [ 
    { "Foo" : {"Param1": {"value": "aaa", "Type": "string"}}, {"Param2": {"value": "bbb", "Type": "string"}} },
    { "Foo" : {"Param1": {"value": "ccc", "Type": "string"}}, {"Param2": {"value": "ddd", "Type": "string"}} }
]}

我已尝试消化与此挑战相关的许多其他示例 (1,2),但正在努力适应它们。据我所知,这是:

private void WriteStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders)
{
    using (StringWriter _stringWriter = new StringWriter()) {
        if (!(value is ICollection))
        {
            foreach (PropertyInfo p in value.GetProperties())
            {
                _stringWriter.Write(GetPropertyData(p));
            }
        }
        else
        {
            foreach (object o in value)
            {
                foreach (PropertyInfo p in o.GetProperties())
                {
                    _stringWriter.Write(GetPropertyData(p));
                }
            }
        }
        // output omitted for brevity...
    }
}

public function GetPropertyData(PropertyInfo p) {
    return string.Format("{name: '{0}', type: '{1}', value: '{2}'},", 
        p.Name, 
        p.PropertyType.ToString(), 
        p.GetValue(p).ToString())
}

我相信您正在以错误的方式解决您的问题。与其通过创建自定义来重新发明轮子 MediaTypeFormatter,您应该只为您的对象使用正确的模型,让序列化程序完成其余的工作。

一个示例是根据您的目的使用扩展方法:

public static class JsonExtensions
{
    public static object CreateModels<T>(this IEnumerable<T> models, string modelName = null)
    {
        modelName = modelName ?? typeof(T).Name+"Collection";

        return new Dictionary<string, object>()
        {
            { modelName, models.Select(m => CreateModel(m)) }
        };
    }

    public static IDictionary<string, object> CreateModel<T>(this T model, string modelName = null)
    {
        modelName = modelName ?? typeof(T).Name;

        return new Dictionary<string, object>()
        {
            { modelName, GetProperties(model) }
        };
    }

    private static IDictionary<string, object> GetProperties<T>(T obj)
    {
        var props = typeof(T).GetProperties();
        return props.ToDictionary(p => p.Name, p => (object)new { type = p.PropertyType.ToString(), value = p.GetValue(obj, null).ToString() });
    }
}

假设您在项目中使用 Json.NET,它们的使用方式如下:

JsonConvert.SerializeObject(new MyPerson { FirstName = "Bob", DateOfBirth = Convert.ToDateTime("1979-03-01") }.CreateModel());

输出(漂亮的打印):

{
  "MyPerson": {
    "FirstName": {
      "type": "System.String",
      "value": "Bob"
    },
    "DateOfBirth": {
      "type": "System.DateTime",
      "value": "3\/1\/1979 12:00:00 AM"
    }
  }
}

同时:

JsonConvert.SerializeObject(new List<Foo>() {
    new Foo() { Param1 = "aaa", Param2 = "bbb" },
    new Foo() { Param1 = "ccc", Param2 = "ddd" }
}.CreateModels());

输出:

{
  "FooCollection": [
    {
      "Foo": {
        "Param1": {
          "type": "System.String",
          "value": "aaa"
        },
        "Param2": {
          "type": "System.String",
          "value": "bbb"
        }
      }
    },
    {
      "Foo": {
        "Param1": {
          "type": "System.String",
          "value": "ccc"
        },
        "Param2": {
          "type": "System.String",
          "value": "ddd"
        }
      }
    }
  ]
}

.NET Fiddle 演示 HERE

备注

我在您的示例中看到您使用 string 而不是 System.String 作为属性的类型名称。使用别名类型名称而不是 CLR 真实名称并不容易,但如果确实有必要,您可以查看 this answer 以获得该名称。