Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将JSON反序列化为泛型,其中集合属性名称根据类型更改_C#_.net_Json_Json.net - Fatal编程技术网

C# 将JSON反序列化为泛型,其中集合属性名称根据类型更改

C# 将JSON反序列化为泛型,其中集合属性名称根据类型更改,c#,.net,json,json.net,C#,.net,Json,Json.net,我正在使用Newtonsoft.Json解析一些API响应,它们似乎有一个非常规则的结构,除了根据返回类型更改的数组属性名。几个响应示例(假/空数据): 客户: { "success": true, "message": "Records Retrieved Successfully", "data": { "total_count": "1", "customer": [ { "id": "1234", "accountI

我正在使用Newtonsoft.Json解析一些API响应,它们似乎有一个非常规则的结构,除了根据返回类型更改的数组属性名。几个响应示例(假/空数据):

客户:

{
  "success": true,
  "message": "Records Retrieved Successfully",
  "data": {
    "total_count": "1",
    "customer": [
      {
        "id": "1234",
        "accountId": "220",
        "email": "json.voorhees@lycos.com",
        "name": "JSON Voorhees",
        "company": "Test Company",
        "customFieldsValues": [
          {
            "value": "Some Guy",
            "field": {
              "id": "69",
              "name": "SalespersonID",
              "label": "Account Manager"
            }
          }
        ]
      }
    ]
  }
}
{
  "success": true,
  "message": "Records Retrieved Successfully",
  "data": {
    "total_count": "0",
    "invoice": []
  }
}
发票:

{
  "success": true,
  "message": "Records Retrieved Successfully",
  "data": {
    "total_count": "1",
    "customer": [
      {
        "id": "1234",
        "accountId": "220",
        "email": "json.voorhees@lycos.com",
        "name": "JSON Voorhees",
        "company": "Test Company",
        "customFieldsValues": [
          {
            "value": "Some Guy",
            "field": {
              "id": "69",
              "name": "SalespersonID",
              "label": "Account Manager"
            }
          }
        ]
      }
    ]
  }
}
{
  "success": true,
  "message": "Records Retrieved Successfully",
  "data": {
    "total_count": "0",
    "invoice": []
  }
}
您会注意到,在第一个数组中,数组的属性名称是“customer”,在第二个数组中,它是“invoice”(我们没有任何invoice,所以我还不知道该对象的确切结构)

我的最终目标是反序列化到类结构中,如下所示:

public class Response {
    [JsonProperty("success")]
    public bool Success { get; set; }
    [JsonProperty("message")]
    public string Message { get; set; }
}

public class Response<T> : Response {
    public List<T> Data { get; set; }
}

实现这一点最明智的方法是什么?

JavaScriptDeserializer对象作为
动态对象在这里可能会帮助您:

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });

dynamic obj = serializer.Deserialize(json, typeof(object));
然后,获取所有属性以测试对象具有哪些属性:

var propertyInfo = obj.GetType().GetProperties();
然后,获取所需的属性名称并将其传入:

var value = obj.data[0].GetType().GetProperty(propertyName).GetValue(obj, null);
作为一个循环示例:

foreach (var property in obj.GetType().GetProperties()) {
    Console.WriteLine(String.Format("The value for property {0} is {1}.",
        property.Name,
        obj.data[0].GetType().GetProperty(propertyName).GetValue(obj, null));
}

注意:这个答案使用的是
System.Reflection
,这对于大型计算来说非常缓慢(也就是说,除非你有空闲时间消磨时间,否则不要迭代这些方法数千次!)

好吧,这是可行的,但它肯定不是我见过的最漂亮的东西(对转换成字典的中间步骤不太满意)。暂时把这个问题留着,以防有人知道更好的方法

各种对象:

public class Response {
    [JsonProperty("success")]
    public bool Success { get; set; }
    [JsonProperty("message")]
    public string Message { get; set; }
}

public class Response<T> : Response {
    [JsonProperty("data")]
    [JsonConverter(typeof(DataConverter))]
    public List<T> Data { get; set; }
}

public class DataConverter : JsonConverter {
    public override bool CanConvert(Type objectType) {
        return typeof(List<object>).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        Dictionary<string, object> data = (Dictionary<string, object>)serializer.Deserialize(reader, typeof(Dictionary<string, object>));
        foreach (KeyValuePair<string, object> kvp in data) {
            if (kvp.Key != "total_count") {
                return ((JToken)kvp.Value).ToObject(objectType);
            }
        }

        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        throw new NotImplementedException();
    }
}
公共类响应{
[JsonProperty(“成功”)]
公共bool成功{get;set;}
[JsonProperty(“消息”)]
公共字符串消息{get;set;}
}
公共课回应:回应{
[JsonProperty(“数据”)]
[JsonConverter(类型(数据转换器))]
公共列表数据{get;set;}
}
公共类数据转换器:JsonConverter{
公共覆盖布尔CanConvert(类型objectType){
返回typeof(List).IsAssignableFrom(objectType);
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序){
字典数据=(字典)序列化程序。反序列化(读取器,typeof(字典));
foreach(数据中的KeyValuePair kvp){
如果(kvp.Key!=“总计数”){
返回((JToken)kvp.Value).ToObject(objectType);
}
}
返回null;
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器){
抛出新的NotImplementedException();
}
}
然后获取响应:

public Response<Customer> GetCustomers() {
    string response = SendRequest("/api/v1/customers");
    Response<Customer> aresponse = JsonConvert.DeserializeObject<Response<Customer>>(response);
}
公共响应GetCustomers(){
字符串响应=SendRequest(“/api/v1/customers”);
Response aresponse=JsonConvert.DeserializeObject(响应);
}

我不太清楚你问“最理智”是什么意思解决此问题的方法。Json.NET支持将意外属性捕获到
字典
字典
中。但是,您的
列表数据
是类型化的,Json.NET没有内置将任意命名的属性反序列化为类型化对象的功能。您还编写了一个不完全满意的中间step转换为字典的方法,因此听起来好像您想要一个避免反序列化为中间表示的解决方案。以下转换器可实现此目的:

[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class JsonAnyPropertyNameAttribute : System.Attribute
{
}

class JsonAnyPropertyNameConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException("This converter is intended to be applied directly to a type or a property.");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        try
        {
            int defaultCount = 0;
            var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
            if (existingValue == null)
                existingValue = contract.DefaultCreator();
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonToken.Comment:
                        break;
                    case JsonToken.PropertyName:
                        {
                            var name = reader.Value.ToString();
                            var property = contract.Properties.GetClosestMatchProperty(name);
                            if (!reader.Read())
                                throw new JsonSerializationException(string.Format("Missing value at path: {0}", reader.Path));
                            if (property == null)
                            {
                                property = contract.Properties.Where(p => p.AttributeProvider.GetAttributes(true).OfType<JsonAnyPropertyNameAttribute>().Any()).Single();
                                defaultCount++;
                                if (defaultCount > 1)
                                {
                                    throw new JsonSerializationException(string.Format("Too many properties with unknown names for type {0} at path {1}", objectType, reader.Path));
                                }
                            }
                            var value = serializer.Deserialize(reader, property.PropertyType);
                            property.ValueProvider.SetValue(existingValue, value);
                        }
                        break;
                    case JsonToken.EndObject:
                        return existingValue;
                    default:
                        throw new JsonSerializationException(string.Format("Unknown token {0} at path: {1} ", reader.TokenType, reader.Path));
                }
            }
            throw new JsonSerializationException(string.Format("Unclosed object at path: {0}", reader.Path));
        }
        catch (Exception ex)
        {
            if (ex is JsonException)
                throw;
            // Wrap any exceptions encountered in a JsonSerializationException
            throw new JsonSerializationException(string.Format("Error deserializing type {0} at path {1}", objectType, reader.Path), ex);
        }
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

如果有多个未知属性,转换器将抛出异常,而不是覆盖以前反序列化的数据。

谢谢,看起来它可能会起作用,不过如果可能的话,我希望避免反射,因为我不知道数据集最终会有多大(在本项目中,或将来的项目中).可能是这样,但是浏览
JsonObject
而不是反射类型?你说的“sanest”是什么意思?你的转换器很简洁。你是在寻找一种避免加载中间表示的解决方案吗?@dbc只是想知道是否有更好的方法来实现它,而不需要先反序列化到字典,然后从字典中挑选。如果这是最简单的方法,我可以接受。