将Json反序列化为对象和返回的子对象

本文关键字:对象 返回 反序列化 Json | 更新日期: 2023-09-27 17:50:33

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

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

因此,对于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到两个对象。

编辑

为那些想做类似事情的人贴出答案

欢呼

将Json反序列化为对象和返回的子对象

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

这是我的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();
    }
}

为了使用上面的内容,我创建了一个泛型方法,看起来像下面这样,允许我传递命令来运行,额外的查询字符串和类型来转换'Data'对象:

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方法,我只是简单地传递命令,查询和类型的数据对象转换成,无论它是什么。

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结构,如果有人看到一个更简单的方法来实现转换器,我很高兴接受任何输入。

欢呼

要了解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; }
}

两个实体:

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);

可以看到没有附加的包