1

I'm a beginner in C # and Xamarin. I'm trying to deserialize json arrays with newtonsoft? Here is my json file:

{
    "next": {
        "$ref": "http://192.168.0.100:8080/ords/hr/employees/?page=1"
    },
    "items": [
        {
            "empno": 7369,
            "ename": "SMITH",
            "job": "CLERK",
            "mgr": 7902,
            "sal": 800,
            "deptno": 20
        },
        {
            "empno": 7934,
            "ename": "MILLER",
            "job": "CLERK",
            "mgr": 7782,
            "sal": 1300,
            "deptno": 10
        }
    ]
}

Her is my model class:

public class RootObject
    {
        [JsonProperty("items")]
        public Item[] Items { get; set; }

        [JsonProperty("next")]
        public Next Next { get; set; }
    }

    public class Next
    {
        [JsonProperty("$ref")]
        public string Ref { get; set; }
    }

    public class Item
    {
        [JsonProperty("deptno")]
        public long Deptno { get; set; }

        [JsonProperty("empno")]
        public long Empno { get; set; }

        [JsonProperty("ename")]
        public string Ename { get; set; }

        [JsonProperty("job")]
        public string Job { get; set; }

        [JsonProperty("mgr")]
        public long Mgr { get; set; }

        [JsonProperty("sal")]
        public long Sal { get; set; }
    }

When I try deserialize into a List it throws exception on this line:

var data = JsonConvert.DeserializeObject<List<RootObject>>(json);

The error is:

Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[System.Object]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.

This code desirialize the Json, but I don't now retrive the data and populate the class:

public class ApiServices    {


        public async Task<RootObject> GetRootObject()
        {
            var client = new HttpClient();
            var url = string.Format("http://mysite/ords/hr/employees");
            var response = await client.GetAsync(url);

            var json = await response.Content.ReadAsStringAsync();

           dynamic jsonDe = JsonConvert.DeserializeObject<RootObject>(json);

            return jsonDe;
        }

I have already created some code in MainViewModel, but I do not know how to retrieve the data and insert the class Item:

public class MainViewModel
    {
        #region Properties
     
        public ObservableCollection<RootItemViewModel> RootObjects { get; set; }

        private ApiServices apiServices;

        #endregion

        #region Constructors
        public MainViewModel()
        {
            //Create observable collections
            RootObjects = new ObservableCollection<RootItemViewModel>();

            //Instance services
            apiServices = new ApiServices();

            //Load Data
            LoadData();
        }
        #endregion

        #region Methods


        private async void LoadData()

        {
            var emps = new RootObject();
                      
            emps = await apiServices.GetRootObject();

            RootObjects.Clear();

            foreach (var item in RootObjects)
            {

                RootObjects.Add(new RootItemViewModel
                {


                });
            }
        }
            
        #endregion
    }
}

The class RootItemViewModel:

    public class RootItemViewModel : RootObject
    {

       
    }
Community
  • 1
  • 1
  • 3
    try JsonConvert.DeserializeObject(json); your json top level is an object not a list. – Cedar Dec 03 '17 at 04:50

3 Answers3

0

For this JSON:

{
"next": {
    "$ref": "http://192.168.0.100:8080/ords/hr/employees/?page=1"
},
"items": [
    {
        "empno": 7369,
        "ename": "SMITH",
        "job": "CLERK",
        "mgr": 7902,
        "sal": 800,
        "deptno": 20
    },
    {
        "empno": 7934,
        "ename": "MILLER",
        "job": "CLERK",
        "mgr": 7782,
        "sal": 1300,
        "deptno": 10
    }
]

}

Use this Class:

internal class SomeObject{ //in C# you can even NOT to define internal as well :)
public class next{
    public string ref {get; set;} //var do not use $, you need to change the JSON code or you nned to make a custom reader for that.
}

public List<Item> items {get; set;}

public class Item{
    public string empno {get; set;}
    public string ename {get; set;}
    public string job {get; set;}
    public string mgr {get; set;}
    public string sal {get; set;}
    public string deptno {get; set;}
}
}

To deserialize those JSON to a Class, use this one:

    var obj = JSONConvert.DeserializeObject<SomeObject>(json); //json is a string of your JSON

//Now you can access the data like 

listView.ItemsSource = obj.Items; //for mobile listview

You cannot put List because the JSON object is not pure List Of object, it is combined object between next object and items object. So you need to create a class first before putting the JSON into list.

Mr Hery
  • 829
  • 1
  • 7
  • 25
0

http://json2csharp.com/ is always useful, ( quick way to get the class 'skeleton' )

like 'Mr Hery' wrote you might have to do a custom reader or remove the $ sign - if you are generating the json output yourself.

public class Next
{
    public string @ref { get; set; }
}

public class Item
{
    public int empno { get; set; }
    public string ename { get; set; }
    public string job { get; set; }
    public int mgr { get; set; }
    public int sal { get; set; }
    public int deptno { get; set; }
}

public class RootObject //(rename class you something better like Employees)
{
    public Next next { get; set; }
    public List<Item> items { get; set; }
}

For Deserializing :

var data = JsonConvert.DeserializeObject<RootObject>(json);

or

var _employees = JsonConvert.DeserializeObject<Employees>(json);
bomanden
  • 314
  • 1
  • 2
  • 16
0

1) Your Json response shows, that it just holds an instance of the type RootObject.

2) var data = JsonConvert.DeserializeObject<List<RootObject>>(json); will not work therefore, because you try to cast a RootObject to a List<RootObject>. Described in your error response.

3) dynamic jsonDe = JsonConvert.DeserializeObject<RootObject>(json); will work because here you cast a RootObject to a RootObject. I also suggest you tu use "var" instead of "dynamic" (see why: dynamic vs var)

4) your following method seems to have a mistake in there:

private async void LoadData()
{
    var emps = new RootObject();

    emps = await apiServices.GetRootObject();

    RootObjects.Clear();

    //Mistake:
    //You iterate through the previously cleared observable collection.
    //it really should be "foreach (var item in emps.Items)"
    foreach (var item in RootObjects)
    {

        RootObjects.Add(new RootItemViewModel
        {


        });
    }
}

5) It looks like you wanted to feed the observable collection with the array of type Item on your RootObject:

public class RootObject
    {
        [JsonProperty("items")]
        public Item[] Items { get; set; } //This property

        [JsonProperty("next")]
        public Next Next { get; set; }
    }

So what you actually should be doing is setting the data like this:

private async void GetRootObjectItems()
        {
            RootObject emps = await apiServices.GetRootObject();   //Get the root object
            RootObjects.Clear();                        //clear the list

            foreach (var item in emps.Items)    //Important, to get the data from the Array on the RootObject
            {
                ///its a nice way to give your viewmodel a ctor signature that ensures a correct initialization
                ///public RootItemViewModel(Item item)
                ///{ 
                /// this.Deptno = item.Deptno;
                ///}
                RootObjects.Add(new RootItemViewModel(item));  
            }
        }
    }

6) It makes no sense for your RootItemViewModel to inherit from RootObject. You should make it inherit from Item or have it hold a property of type Item.

I hope I could clear things up!

Csharpest
  • 1,258
  • 14
  • 32
  • 1
    Great example! thank you. The code worked! and my view is binding to the viewmodel – Alex Ribeiro Dec 04 '17 at 17:42
  • Fantastic! Thank you too – Csharpest Dec 04 '17 at 17:47
  • please can you explain better... This part `RootObjects.Add(new RootItemViewModel(item));` in my code is showing error that **RootItemViewModel does not contain a constructor that takes 1 arguments** – DevLayi Sep 29 '20 at 15:10
  • So sorry to disturb. I realized my viewModel was pointing to another collection fro the start. I've solved the issue – DevLayi Sep 29 '20 at 15:23
  • So sorry to disturb. I realized my viewModel was pointing to another collection fro the start. I've solved the issue – DevLayi Sep 29 '20 at 15:23