【问题标题】:Deserialize Json into object and child object for return将Json反序列化为对象和子对象返回
【发布时间】:2014-03-16 13:10:48
【问题描述】:

对 JSON 不太熟悉,遇到了一个对我来说并不明显的问题。

我正在查询的 api 返回一个标准响应对象,其中处理的命令/api 请求的结果嵌入在 json 中的数据对象中。

因此,对于 API 上的所有请求,响应返回如下所示,数据组件会根据请求的内容而变化。

ObjectType1 响应

{
    "data": { 
        "person" : { 
            "id" : 21, 
            "name" : "Json can be annoying at times"
        }
    },
    "message" : "",
    "result" : "success"
}

或者其他 api 请求将返回以下列表

ObjectType2 响应

{
    "data": { 
        "1234" : {
            "id": 1234,
            "title" : "Terminator"

        },
        "3245" : { 
            "id" : 3245, 
            "name" : "Terminator 2"
        }
    },
    "message" : "",
    "result" : "success"
}

我想要一个自定义的 JsonConverter 将响应拉出到这样的对象中

public class MyResponse {

    [JsonProperty(PropertyName = "data")]
    public string Data { get; set; }

    [JsonProperty(PropertyName = "message")]
    public string Message { get; set; }

    [JsonProperty(PropertyName = "status")]
    public string Status { get; set; }
}

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

    public string Message { get; set; }

    public string Status { get; set; }
}

然后从那里我可以在一个通用方法中对状态/消息采取行动,然后将一个 json 字符串返回给我的库中的调用方法。从中可以根据请求正确处理返回的json字符串。

任何想法如何将数据的子对象反序列化回字符串,或者如果我将方法传递给通用类型 T 甚至更好,我如何将 json 反序列化为两个对象。

编辑

下面为那些想要做类似事情的人发布答案

干杯

【问题讨论】:

    标签: c# json deserialization json-deserialization


    【解决方案1】:

    感谢那些提供帮助的人,但我最终想出了我正在寻找的答案,它将我的对象反序列化为通过泛型提供的适当类型。

    这是我的 MyCustomResponse 对象

    public class MyCustomResponse 
    {
        [JsonProperty(PropertyName = "data")]   
        public object Data { get; set; }
    
        [JsonProperty(PropertyName = "message")]
        public string Message { get; set; }
    
        [JsonProperty(PropertyName = "result")]
        public string Result { get; set; }
    }
    

    自定义的 JsonConverter 就这样结束了,我在 json 字符串“data”中查找属性,然后将其转换为 T 类型的对象

    public class MyCustomResponseConverter<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(MyCustomResponse));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            object instance = objectType.GetConstructor(Type.EmptyTypes).Invoke(null);
            PropertyInfo[] props = objectType.GetProperties();
    
            JObject jo = JObject.Load(reader);
            foreach ( JProperty jp in jo.Properties() )
            {
                PropertyInfo prop = props.FirstOrDefault(pi =>
                    pi.CanWrite && string.Equals(pi.Name, jp.Name, StringComparison.OrdinalIgnoreCase));
    
                if ( prop != null )
                {
                    // Convert data object to what was passed in at T
                    if ( jp.Name == "data" )
                        prop.SetValue(instance, jo.SelectToken("data").ToObject(typeof(T)));
                    else
                        prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
                }
            }
    
            return instance;
        }
    
        public override bool CanWrite { get { return false; } }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    为了使用上述方法,我创建了一个如下所示的通用方法,并允许我传递要运行的命令、额外的查询字符串以及将“数据”对象转换为的类型:

    private async Task<T> GenericApiRequestAsync<T>(string command, string query)
    {
        HttpClient client = new HttpClient();
    
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
        Uri uri = new Uri(string.Format("{0}/api/{1}/?cmd={2}{3}", apiUrl, apiKey, command, query));
    
        try {   
            HttpResponseMessage response = await client.GetAsync(uri);                
            response.EnsureSuccessStatusCode();
    
            var responseContent = await response.Content.ReadAsStringAsync();
    
            // Convert responseContent via MyCustomResponseConverter
            var myCustomResponse = 
                    await Task.Factory.StartNew(() =>
                                           JsonConvert.DeserializeObject<MyCustomResponse(
                                               responseContent, 
                                               new MyCustomResponseConverter<T>()
                                           ));
    
            return (T)myCustomResponse.Data;
        }
        catch(Exception ex)
        {
            ... 
        }
    }
    

    然后要使用实际的 GenericApiRequestAsync 方法,我只需将要转换的 Data 对象的命令、查询和类型传递给它,无论它可能是什么。

    public async Task<Person> GetPersonAsync(int id)
    {
        return await GenericApiRequestAsync<Person>("person.byid", string.Format("&id={0}", id));
    }
    
    public async Task<IDictionary<string, ObjectType2>> GetObjectType2ListAsync(string name)
    {
        return await GenericApiRequestAsync<IDictionary<string, ObjectType2>>("show.byname", string.Format("&name={0}", name));
    }
    

    最终得到了一个简单的解决方案,但实现起来却很复杂。它也消除了在最终对象中再次处理数据对象的需要。

    希望此解决方案可以帮助遇到类似 JSON 结构的其他人,如果有人看到实现转换器的更简单方法,我很乐意接受任何输入。

    干杯

    【讨论】:

      【解决方案2】:

      对于 JSON 对象的序列化/反序列化,请查看Json.NET。您可以将其作为 Nuget 包包含并使用内置方法,例如 JsonConvert.SerializeObject(Object object)JsonConvert.DeserializeObject(string value, Type type)

      您可以通过使用 JsonProperty 属性装饰模型来控制 JSON 属性的名称。例如:

      public class MyResponse {
          [JsonProperty(PropertyName = "data")]
          public string Data { get; set; }
      
          [JsonProperty(PropertyName = "message")]
          public string Message { get; set; }
      
          [JsonProperty(PropertyName = "status")]
          public string Status { get; set; }
      }
      

      【讨论】:

      • 感谢 Scott,我正在使用 Json.Net,而且我的 Response 数据结构与您上面的完全一样。我的问题是可以返回的“数据”对象在调用类型之间发生变化。实际上,我可能只是尝试使用“对象”类型而不是字符串作为数据,然后看看它会把我带到哪里,干杯。
      【解决方案3】:

      两个实体:

      public class Response
      {
          public Dictionary<string, Data> data { get; set; }
          public string message { get; set; }
          public string result { get; set; }
      }
      
      public class Data
      {
          public int id { get; set; }
          public string title { get; set; }
      }
      

      响应码是:

          JavaScriptSerializer serializer = new JavaScriptSerializer();
          Response response = (Response) serializer.Deserialize<Response>(jsonString);
      

      如你所见,没有额外的包

      【讨论】:

      • 感谢 Dmitriy,但是,我希望就这么简单,数据在每个“调用类型”上不断变化,检查我上面传入 json 的 2 种不同类型,数据对象是不同的对象。我意识到上面的签名是相同的,即。 int, string,但这可能是一个糟糕的例子,对象的签名将在大约 20 个对象之间发生巨大变化。
      猜你喜欢
      • 2022-01-14
      • 1970-01-01
      • 2021-12-16
      • 1970-01-01
      相关资源
      最近更新 更多