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

C# 不同自定义类和集合上的反射GetProperties,c#,.net,reflection,asp.net-apicontroller,C#,.net,Reflection,Asp.net Apicontroller,在WebAPI中,我创建了一个自定义函数来创建带有特定模式的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

在WebAPI中,我创建了一个自定义函数来创建带有特定模式的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
我最感兴趣的部分是通过反射获取类属性名称/值

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

return new MyPerson { .FirstName = "Bob", .DateOfBirth = Convert.ToDateTime("1979-03-01") }
…将输出为(粗略的示例,因为JSON很容易更改以创建必要的模式):

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

return new IEnumerable<Foo>() {
    new Foo() { Param1 = "aaa", Param2 = "bbb" },
    new Foo() { Param1 = "ccc", Param2 = "ddd" }
}
我试着去消化与这个挑战相关的许多其他例子,但我正在努力适应它们。这是我所能做到的:

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() });
    }
}
输出(打印精美):

而:

JsonConvert.SerializeObject(new List<Foo>() {
    new Foo() { Param1 = "aaa", Param2 = "bbb" },
    new Foo() { Param1 = "ccc", Param2 = "ddd" }
}.CreateModels());
.NET小提琴演示


我在您的示例中看到,您正在使用
string
而不是
System.string
作为属性的类型名。使用别名类型名而不是CLR真名并不容易,但如果确实有必要,您可以寻找一种方法来获得它。

“编写属性名、类型和值”-我在您的Json示例中看不到该类型。另外,如果属性不是基本类型(int、float、string等),该怎么办?对不起,是的,我的头是从RTFM重载旋转的。经修正的议题。所有属性类型都将是基本类型(字符串、int、double或bool),尽管我想知道如果它们不是基本类型(尽管可能还有另一个问题)该怎么办。存在实用程序从C#类或JSON生成JSON模式。我觉得你在尝试重新设计轮子。你能提供这样一个例子吗?我已经将NewtonSoft等用于一般序列化,但在本例中,模式必须是显式的,并且不是标准的。JSON的生成不是问题,而是如何使用反射检查对象。这很有趣。更大的问题是我需要支持标准格式的JSON,加上Google DataTable格式(不是严格意义上的JSON)[不想单独修改每个控制器以检查所需的输出(昨天询问时还没有输入)…如果不使用格式化程序,我看不出如何做到这一点。Google自己的描述是,“与JSON非常相似,但并不完全相同。",所以我不知道您的解决方案是否可行?Web API已经使用Json.NET序列化对象,因此您应该创建一个操作筛选器,拦截返回的对象并在需要时对其进行更改。您可以从这个或开始。通过这种方法,我假设同一个控制器URL可以返回以不同方式表示的相同数据?如果是,如何执行这是由调用方控制的吗?这取决于您,您可以访问
HttpActionExecutedContext
中的整个HTTP请求。
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() });
    }
}
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"
        }
      }
    }
  ]
}