Shawn C.
Shawn C.

Reputation: 6841

Deserializing JSON Object Array with Json.net

I am attempt to use an API that use the follow example structure for their returned json

[
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account",
         "email":"[email protected]",
         "organization":"",
         "reference":null,
         "id":3545134,
         "created_at":"2013-08-06T15:51:15-04:00",
         "updated_at":"2013-08-06T15:51:15-04:00",
         "address":"",
         "address_2":"",
         "city":"",
         "state":"",
         "zip":"",
         "country":"",
         "phone":""
      }
   },
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account2",
         "email":"[email protected]",
         "organization":"",
         "reference":null,
         "id":3570462,
         "created_at":"2013-08-12T11:54:58-04:00",
         "updated_at":"2013-08-12T11:54:58-04:00",
         "address":"",
         "address_2":"",
         "city":"",
         "state":"",
         "zip":"",
         "country":"",
         "phone":""
      }
   }
]

JSON.net would work great with something like the following structure

{
    "customer": {
        ["field1" : "value", etc...],
        ["field1" : "value", etc...],
    }
}

But I can not figure out how to get it to be happy with the provided structure.

Using the default JsonConvert.DeserializeObject(content) results the correct number of Customer but all of the data is null.

Doing something a CustomerList (below) results in a "Cannot deserialize the current JSON array" exception

public class CustomerList
{
    public List<Customer> customer { get; set; }
}

Thoughts?

Upvotes: 150

Views: 312210

Answers (7)

Tyler Liu
Tyler Liu

Reputation: 20376

For those who don't want to create any models, use the following code:

var result = JsonConvert.DeserializeObject<
  List<Dictionary<string, 
    Dictionary<string, string>>>>(content);

Note: This may not work for your JSON string. This is not a general solution for any JSON structure. It works for the JSON structure in the question. For your own JSON structure, you need to adjust the code.

Upvotes: 49

Stephen Quan
Stephen Quan

Reputation: 26274

Nowadays, you can use System.Text.Json and the JsonSerializer class. Either

CustomerList customers = JsonSerializer.Deserialize<CustomerList>(json);

or

List<Customer> customers = JsonSerializer.Deserialize<List<Customer>>(json);

The former is your own POCO class with a combination of properties which may be scalar or an arrays. The latter, for deserializing a specific array object.

Upvotes: 0

Andrea Omboni
Andrea Omboni

Reputation: 1

Simple: create two classes, one for customer and the other for array object.
Json string like this:

{
  "fieldObjects": [
    {
      "Field1": "ValueField1_a",
      "Field2": "ValueField2_a"
    },
    {
      "Field1": "ValueField1_b",
      "Field2": "ValueField2_b"
    }
  ]
}

In .net you have the first class (parent) for fieldObjects that contains only one property list(of child class). Child class contains properties Field1, Field2,...
Finally use DeserializeObject on Json string using parent class, without using the list of ...
That's all

Upvotes: -1

Joffrey Kern
Joffrey Kern

Reputation: 6499

You can create a new model to Deserialize your JSON CustomerJson:

    public class CustomerJson
    {
        [JsonProperty("customer")]
        public Customer Customer { get; set; }
    }

    public class Customer
    {
        [JsonProperty("first_name")]
        public string Firstname { get; set; }

        [JsonProperty("last_name")]
        public string Lastname { get; set; }

        ...
    }

And you can deserialize your JSON easily:

JsonConvert.DeserializeObject<List<CustomerJson>>(json);

Documentation: Serializing and Deserializing JSON

Upvotes: 222

andmar8
andmar8

Reputation: 331

Further modification from JC_VA, take what he has, and replace the MyModelConverter with...

public class MyModelConverter : JsonConverter
{
    //objectType is the type as specified for List<myModel> (i.e. myModel)
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader); //json from myModelList > model
        var list = Activator.CreateInstance(objectType) as System.Collections.IList; // new list to return
        var itemType = objectType.GenericTypeArguments[0]; // type of the list (myModel)
        if (token.Type.ToString() == "Object") //Object
        {
            var child = token.Children();
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(token.CreateReader(), newObject);
            list.Add(newObject);
        }
        else //Array
        {
            foreach (var child in token.Children())
            {
                var newObject = Activator.CreateInstance(itemType);
                serializer.Populate(child.CreateReader(), newObject);
                list.Add(newObject);
            }
        }
        return list;

    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

This should work for json that is either

myModelList{
 model: [{ ... object ... }]
}

or

myModelList{
 model: { ... object ... }
}

they will both end up being parsed as if they were

myModelList{
 model: [{ ... object ... }]
}

Upvotes: 0

JC_VA
JC_VA

Reputation: 27

Slight modification to what was stated above. My Json format, which validates was

{
    mycollection:{[
           {   
               property0:value,
               property1:value,
             },
             {   
               property0:value,
               property1:value,
             }
           ]

         }
       }

Using AlexDev's response, I did this Looping each child, creating reader from it

 public partial class myModel
{
    public static List<myModel> FromJson(string json) => JsonConvert.DeserializeObject<myModelList>(json, Converter.Settings).model;
}

 public class myModelList {
    [JsonConverter(typeof(myModelConverter))]
    public List<myModel> model { get; set; }

}

class myModelConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var list = Activator.CreateInstance(objectType) as System.Collections.IList;
        var itemType = objectType.GenericTypeArguments[0];
        foreach (var child in token.Children())  //mod here
        {
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(child.CreateReader(), newObject); //mod here
            list.Add(newObject);
        }
        return list;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

}

Upvotes: 2

AlexDev
AlexDev

Reputation: 4727

Using the accepted answer you have to access each record by using Customers[i].customer, and you need an extra CustomerJson class, which is a little annoying. If you don't want to do that, you can use the following:

public class CustomerList
{
    [JsonConverter(typeof(MyListConverter))]
    public List<Customer> customer { get; set; }
}

Note that I'm using a List<>, not an Array. Now create the following class:

class MyListConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var list = Activator.CreateInstance(objectType) as System.Collections.IList;
        var itemType = objectType.GenericTypeArguments[0];
        foreach (var child in token.Values())
        {
            var childToken = child.Children().First();
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(childToken.CreateReader(), newObject);
            list.Add(newObject);
        }
        return list;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

Upvotes: 4

Related Questions