Newtonsoft deserialize object that extends from Collection<Item>

限于喜欢 提交于 2019-12-11 06:34:18

问题


I'm trying to use NewtonSoft.Json deserializer, but I don't know if what I'm trying to do is doable, cuase every example of collections that I've seen are like this:

public class ItemRecords 
{
     public List<ItemRecords> Items { get; set; }
     ...
}

And what I want is something that looks like as it's explained below...

I have this two classes:

public class ItemRecords : Collection<ItemRecord>  
{  
    [JsonProperty("property1")]
    public int Property1 { get; set; }  

    [JsonProperty("property2")]
    public int Property2 { get; set; }  
}

public class ItemRecord
{   
    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonProperty("item_prop1")]
    public string ItemProp1 { get; set; }
    ...
}

I get this json back from my api:

{  
    property1: 25,
    property2: 27,
    item_record: [
       {
           id: 241,
           item_prop1: "0.132",
           item_prop2: "78787",
           ...
       },
       {
           id: 242
           item_prop1: "1212",
           item_prop2: "3333",
           ...
       }
       ...
    ]
}

ItemRecords is a collection of ItemRecord.
I tried by adding the JsonArray attribute to the ItemRecords class as follows:

[JsonArray(ItemConverterType = typeof(ItemRecord))]
public class ItemRecords : Collection<ItemRecord> { ... }

Here is the method that executes the request:

private static async Task<T> MakeRequest<T>(string urlRequest)
{
    try
    {
        HttpWebRequest request = WebRequest.Create(urlRequest) as HttpWebRequest;

        using (WebResponse response = await request.GetResponseAsync())
        {
            using (Stream stream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(stream);

                string line = string.Empty;
                StringBuilder sb = new StringBuilder();

                while ((line = reader.ReadLine()) != null)
                {
                    sb.Append(line);
                }

                T objectDeserialized = JsonConvert.DeserializeObject<T>(sb.ToString());                        

                return objectDeserialized;
            }
        }
    }
    catch (Exception ex)
    {
        return default(T);
    }
}

And call to this method looks like this:

...
return await MakeRequest<ItemRecords>(request);

I don't really know what else to do.. Any help would be appreciated.


回答1:


Your basic difficulty is that the JSON standard has two types of container:

  • The object which is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Json.NET maps dictionaries and non-enumerable POCOS to objects by default, using reflection to map c# properties to JSON properties.

    In the JSON you are getting back from your API, the outermost container is an object.

  • The array which is an ordered collection of values. An array begins with [ (left bracket) and ends with ] (right bracket). Values are separated by , (comma). Json.NET maps non-dictionary enumerables to arrays by default, serializing each collection item as an array entry.

    In the JSON you are getting back from your API, the value of the item_record property is an array.

As a collection with properties, your ItemRecords cannot be mapped to either of these constructs automatically without losing data. Since Json.NET serializes collections as arrays by default, you will need to manually inform it that your type is to be serialized as an object by applying the [JsonObject] attribute. Then, introduce a synthetic item_record property to serialize and deserialize the collection items. Since you are inheriting from Collection<T>, you can use Collection<T>.Items for this purpose:

[JsonObject(MemberSerialization.OptIn)]
public class ItemRecords : Collection<ItemRecord>  
{  
    [JsonProperty("property1")]
    public int Property1 { get; set; }  

    [JsonProperty("property2")]
    public int Property2 { get; set; }  

    [JsonProperty("item_record")]
    IList<ItemRecord> ItemRecordList
    {
        get 
        {
            return Items;
        }
    }
}

Using MemberSerialization.OptIn prevents base class properties such as Count from being serialized.

Sample fiddle.

As an aside, I don't particularly recommend this design, as it can cause problems with other serializers. For instance, XmlSerializer will never serialize collection properties; see here or here. See also Why not inherit from List?.




回答2:


Simply add a List<ItemRecord> Records property to class ItemRecords:

public class ItemRecords
{
    [JsonProperty("property1")]
    public int Property1 { get; set; }  

    [JsonProperty("property2")]
    public int Property2 { get; set; }  

    [JsonProperty("item_record")]
    public List<ItemRecord> Records { get; set; } 
}



回答3:


This looks like you can have a dynamic number of results of properties and item properties like:

{  
    property1: 25,
    property2: 27,
    property3: 30,
    item_record: [
       {
           id: 241,
           item_prop1: "0.132",
           item_prop2: "78787"
       },
       {
           id: 242
           item_prop1: "1212",
           item_prop2: "3333",
           item_prop3: "3333",
           item_prop4: "3333",
       }
    ] }

If this is the case, the best option in my opinion will be to change the structure to something like:

{
    properties:[
       25,
       27,
       30
    ],
    itemRecords:[
        {
            id: 27,
            itemProperties:[
                "321",
                "654",
                "564"
            ]
        },
        {
            id: 25,
            itemProperties:[
                "321",
                "654"
            ]
        },
    ]
}


来源:https://stackoverflow.com/questions/38665313/newtonsoft-deserialize-object-that-extends-from-collectionitem

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!