1

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.

fabricio
  • 1,317
  • 17
  • 20

3 Answers3

1

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; } 
}
Matias Cicero
  • 21,834
  • 10
  • 67
  • 132
  • yes, that's the solution I have found everywhere and I'm mentioning at the top of my question.. Just wanted to know if it was possible to do it given my design – fabricio Jul 29 '16 at 18:43
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?.

dbc
  • 80,875
  • 15
  • 141
  • 235
  • Thanks for your detailed explanation. Although I thought it was a good design you have proved me wrong with the linked questions. – fabricio Jul 29 '16 at 19:22
0

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"
            ]
        },
    ]
}