使用JsonConverter json.net将JSON反序列化为对象列表

时间:2018-08-02 19:54:45

标签: c# json.net

我想反序列化JSON到对象列表,JSON具有这样的结构。

{
    "metadata":{ I don't care about metadata },
    "results": [
        { object that I really want },
        { object that I really want },
        { object that I really want }
            ...
    ]
}

我只想获取results节点内的对象列表,并且由于我想自己反序列化某些属性,因此我使用Alain's answer in "How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?"中的实现来实现JsonConverter,他创建了JsonConverter派生的名为JsonCreationConverter<T>的泛型类,该类具有保护抽象的Create方法,该方法实际上反序列化了JSON,而JSON又被JsonConverter的{​​{1}}调用。

我的派生类的签名及其ReadJson签名就像

Create

我像这样使用它

public class BoringTypeConverter: JsonCreationConverter<List<BoringType>>
{
    protected override List<BoringType> Create(Type objectType, JObject jObject)
    {
        List<BoringType> boringTypes = new List<BoringType>();
        JArray results = (JArray)jObject["results"];

        // deserialize logic
        ...

        return boringTypes;
    }
}

在调试测试时,我发现JsonConvert.DeserializeObject<List<BoringType>>(jsonString, new BoringTypeConverter()); 方法成功地将JSON反序列化为Create,但是当我点击List<BoringType>时,我得到了错误serializer.Populate(jObjectReader, target)

所以我想知道这是问题所在吗?
Cannot populate JSON object onto type 'System.Collections.Generic.List1[BoringType]'. Path 'metadata', line 2, position 15.方法没有对Create字段做任何事情,那么为什么它抱怨metadata

2 个答案:

答案 0 :(得分:2)

我创建了一个简单的示例,您将如何做到这一点:

  1. JsonBase是负责数据反序列化的类

    public static class JsonBase<T> where T: BaseData<T> 
    {
        public static List<T> ReturnResultsList(string json)
        {
            var returnData = JsonConvert.DeserializeObject<BaseData<T>>(json);
            return returnData.results;
        }
        public static  string ReturnMetaData(string json)
        {
            var returnData = JsonConvert.DeserializeObject<BaseData<T>>(json);
            return returnData.metadata;
        }
    }
    
  2. BaseData是类,因为它是通用的,所以可能包含不同类型的数据,并且它包含与JSON相同的属性

    public class BaseData<T> where T: class 
    {
        public string metadata { get; set; }
        public List<T> results { get; set; }
    }
    
  3. SomeObjectTHatUwant是您真正想要的对象

    public class SomeObjectTHatUwant:BaseData<SomeObjectTHatUwant>
    {
        public string category { get; set; }/// property for my case
        public string quantity { get; set; }
    
    }
    
  4. 在您的存储库或某个类中,放入以下方法和字段:

    string url = "http://readabook.16mb.com/api/allcategory";///this link return an json
    
    List<SomeObjectTHatUwant> Response = new List<SomeObjectTHatUwant>();///the data you want
    
    private async Task LoadDataAsync(string uri)
    {
        string responseJsonString = null;
        using (var httpClient = new WebClient())
        {
            try
            {
                responseJsonString = httpClient.DownloadString(uri);
                Response = JsonBase<SomeObjectTHatUwant>.ReturnResultsList(responseJsonString);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
    
  5. 调用LoadDataAsync(url);,此方法在我们的案例Respone字段中初始化, 这是SomeObjectTHatUwant的列表。

(PS。对于方法LoadedDataAsync,我使用库System.Net.Http和System.Threading.Tasks) 代码示例:https://github.com/IonCojucovschi/JsonDeserializeGenericForm

希望获得帮助。

答案 1 :(得分:1)

正如@dbc向我指出的那样,问题How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?实际上是关于如何告诉JsonConvert.DeserializeObject应该反序列化为哪种类型的对象。

所以,我实际上是在对对象进行反序列化,然后告诉JsonConvert将JSON放入我的BoringType,其中不包含metadata字段。因此,为什么我得到错误的字面意思是它无法将JSON放入没有相应字段的类中。

事实证明,编写自定义JsonConverter非常简单(一旦您知道LINQ的JSON)。
我的转换器看起来像这样。

public class BoringTypeConverter : JsonConverter<List<BoringType>>
{
    public override bool CanRead => true;

    public override List<BoringType> ReadJson(JsonReader reader, Type objectType, List<BoringType> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        List<BoringType> boringTypes = new List<BoringType>();

        var jObject = JObject.Load(reader);
        JArray results = (JArray)jObject["results"];

        foreach (var bor in results)
        {
            var boring = new BoringType();

            var region = (string)bor["region"];
            var source = (string)bor["source"];
            JToken source = (string)bor["source"];
            JToken target = (string)bor["target"];

            boring.Region = region;
            boring.Source = source;
            boring.Source = (string)source["id"];
            boring.Target = (string)target["id"];

            boringTypes.Add(boring);
        }

        return boringTypes;
    }

    public override void WriteJson(JsonWriter writer, List<BoringType> value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}