将Json反序列化为对象和子对象以进行返回

时间:2014-03-16 13:10:48

标签: c# json deserialization json-deserialization

对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; }
}

然后从那里我可以在泛型方法中对Status / message执行操作,然后将json字符串返回给我库中的调用方法。从中可以根据请求正确处理返回的json字符串。

任何想法如何将数据的子对象反序列化为字符串,甚至更好,如果我将方法传递给泛型类型T我怎样才能将json反序列化为两个对象。

修改

为那些希望做类似事情的人发表以下答案

干杯

3 个答案:

答案 0 :(得分: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();
    }
}

为了使用上面的内容,我创建了一个类似于以下内容的Generic方法,允许我传递命令来运行,额外的查询字符串以及Type以将'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方法,我只需传递要转换成的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结构的人,如果有人看到更简单的方法来实现转换器,我很乐意接受任何输入。

干杯

答案 1 :(得分:0)

对于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; }
}

答案 2 :(得分:0)

两个实体:

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

如您所见,没有其他套餐