map json DeserializeObject into multi model class - c#

Error:
An unhandled exception occurred while processing the request.
JsonSerializationException: Cannot deserialize the current JSON object
(e.g. {"name":"value"}) into type
'System.Collections.Generic.List`1[Project.Models.Hourly_Units]'
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. Path 'hourly_units.time', line 1,
position 194.
About project: I am getting json data, mapping to model class. than using controller to get json data and displaying in View. I can get single values working in View but having issue displaying List<Hourly_Units> and List<Hourly>
Step#1 - json data
Step#2 - Model Class
public class WeatherModel
{
public double latitude { get; set; }
public double longitude { get; set; }
public double generationtime_ms { get; set; }
public double utc_offset_seconds { get; set; }
public string timezone { get; set; }
public string timezone_abbreviation { get; set; }
public string elevation { get; set; }
public List<Hourly_Units> hourly_units { get; set; }
public List<Hourly> hourly { get; set; }
}
public class Hourly_Units
{
public string time { get; set; }
public string temperature_2m { get; set; }
}
public class Hourly
{
public List<string> time { get; set; }
public List<double> temperature_2m { get; set; }
}
Step#3 - Controller
public async Task<IActionResult> Index()
{
//Weather API
WeatherModel MyWeatherData = new WeatherModel();
MyWeatherData = await GetMyWeather();
return View(MyWeatherData);
}
public async Task<WeatherModel> GetMyWeather()
{
var latitude = 40.712776;
var longitude = -74.005974;
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("https://api.open-meteo.com");
var response = await client.GetAsync($"/v1/forecast?latitude={latitude}&longitude={longitude}&hourly=temperature_2m");
response.EnsureSuccessStatusCode();
var stringResult = await response.Content.ReadAsStringAsync(); //get json data in string
var rawWeather = JsonConvert.DeserializeObject<WeatherModel>(stringResult); // convert json data into objects
return rawWeather;
}
catch (HttpRequestException httpRequestException)
{
throw new Exception($"Error getting weather from OpenWeather: {httpRequestException.Message}");
}
}
}//end of method
Step#4 - View
#model WeatherModel
<form asp-controller="Home" asp-action="Index">
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<p>latitude: #Model.latitude</p>
<p>longitude: #Model.longitude</p>
<p>generationtime_ms: #Model.generationtime_ms</p>
<p>utc_offset_seconds: #Model.utc_offset_seconds</p>
<p>timezone: #Model.timezone</p>
<p>timezone_abbreviation: #Model.timezone_abbreviation</p>
<p>elevation: #Model.elevation</p>
<p>
#foreach (var item in Model.hourly_units)
{
#Html.DisplayFor(modelItem => item.time)
#Html.DisplayFor(modelItem => item.temperature_2m)
}
</p>
<p>
#foreach (var item in Model.hourly)
{
#Html.DisplayFor(modelItem => item.time)
#Html.DisplayFor(modelItem => item.temperature_2m)
}
</p>

Fix the model,you hourly_units property is not a collection, this is what is an error message about
public class WeatherModel
{
//another properties
public Hourly_Units hourly_units { get; set; }
public List<Hourly> hourly { get; set; }
}
public class Hourly
{
public string time { get; set; }
public string temperature_2m { get; set; }
}
fix the action code, parse your json string and convert to the new data model
var jObj = JObject.Parse(stringResult);
var jArr = new JArray();
for (int i = 0; i < ((JArray)jObj["hourly"]["time"]).Count; i++)
{
jArr.Add(new JObject
{
["time"] = (string)((JArray)jObj["hourly"]["time"])[i],
["temperature_2m"] = (double)((JArray)jObj["hourly"]["temperature_2m"])[i]
});
}
jObj["hourly"] = jArr;
var rawWeather = jObj.ToObject<WeatherModel>();
and fix the view code according to the new model too.

Related

How to structure my models to match my JSON?

I'm just new to API and I wanted to consume third party API that was provided to me(which I don't have any control on how they structure the datas). I have asked on S.O before this post but I can't understand what or how should I do their suggestions 'Structure your classes to match your JSON Object'
My Json looks like this :
{
"status": 1,
"message": "",
"data": {
"VacationLeave": 11,
"SickLeave": 10,
"EmergencyLeave": 2,
"HolidaySwap": 1,
"OldLeave": 1
}
}
And on my controllers I have this
public IActionResult APICall()
{
// Fetch the JSON string from URL.
List<APIResponse> leaves = new List<APIResponse>();
string apiUrl = "http://xxxxx.xxx.xxx/GetLeaveBalance/2170";
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync(apiUrl).Result;
if (response.IsSuccessStatusCode)
{
leaves = JsonConvert.DeserializeObject<List<APIResponse>>(response.Content.ReadAsStringAsync().Result);
}
// Return the Deserialized JSON object.
return Json(leaves);
}
My model classes :
public class APIResponse : LeaveModel
{
public int Status { get; set; }
public string Message { get; set; }
public List<LeaveModel> Leaves;
}
public class LeaveModel
{
public int VacationLeave { get; set; }
public int SickLeave { get; set; }
public int EmergencyLeave { get; set; }
public int HolidaySwap { get; set; }
public int OldSwap { get; set; }
}
My problem is: I get this error :
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[APITest.Models.APIResponse]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
Can anyone help me what I'm missing here?
Thank you
Your APIResponse should support generic.
public class APIResponse<T>
{
public int Status { get; set; }
public string Message { get; set; }
public T Data { get; set; }
}
As the data is a LeaveModel object but not a LeaveModel array.
And deserialize as APIResponse<LeaveModel> type.
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(response.Content.ReadAsStringAsync().Result);
LeaveModel leave = apiResponse.Data;
And would suggest changing the method to asynchronous.
public async Task<IActionResult> APICallAsync()
{
//Fetch the JSON string from URL.
LeaveModel leave = new LeaveModel();
string apiUrl = "http://xxxxx.xxx.xxx/GetLeaveBalance/2170";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(await response.Content.ReadAsStringAsync());
leave = apiResponse.Data;
}
//Return the Deserialized JSON object.
return Json(leave);
}
As per json ApiResponse class must look like this
public class APIResponse : LeaveModel
{
public int Status { get; set; }
public string Message { get; set; }
public LeaveModel data;
}

how to convert Json raw object or property cast in to specific model?

public class TransferJson
{
public object json { get; set; }
public int Id { get; set; } = 0;
public bool IsChanged { get; set; } = false;
public string tempData { get; set; } = string.Empty;
}
public partial class Territory
{
public string TerritoryID { get; set; }
public string TerritoryDescription { get; set; }
public int RegionID { get; set; }
}
i have 2 classes one is basically for transfer data and another is for serialize and deserialize data. i am using object to send data over api
however it is not properly deserialize
Territory territory = new Territory();
territory.TerritoryDescription = "Test";
territory.TerritoryID = "Test";
territory.RegionID = 1;
TransferJson objTrf= new TransferJson();
objTrf.json= territory;
objTrf.Id = 1;
objTrf.IsChanged = false;
var SerializeData = JsonConvert.SerializeObject(objTrf);
var DeserializeData= JsonConvert.DeserializeObject<TransferJson>(SerializeData);
var TerritoryData = DeserializeData.json as Territory; // i am getting null here
var Rawobject= DeserializeData.json as object; // i am also not proper getting data here
The json type is object, and it deserialized as dynamic object, so you should to deserialize again DeserializeData.json to get the expected result, like :
Territory territoryData = JsonConvert.DeserializeObject<Territory>(DeserializeData.json.ToString());
I hope you find this helpful.

One or more errors occurred. (Cannot deserialize the current JSON object (e.g))

Trying to deserialize an array of book objects from the GoogleBook API.
Models: https://pastebin.com/24S16hZc
Confirmed API respons: https://pastebin.com/2q0aFGnf
Booking Page:
public BookingPage()
{
this.InitializeComponent();
ResizeWindow();
UpdateListView();
}
private void UpdateListView()
{
UserList_List.Items.Clear();
foreach (HelperLibrary.UserObject L in App.GlobalUserList)
{
UserList_List.Items.Add($" { L.FirstName } { L.LastName }");
}
//Prepare task
Task<HelperLibrary.Models.GoogleBook.RootObject[] > GetBooksTask = HelperLibrary.Helpers.APIHelper.SearchBooks("Hacker");
GetBooksTask.Wait();
HelperLibrary.Models.GoogleBook.RootObject[] Books = GetBooksTask.Result;
foreach(HelperLibrary.Models.GoogleBook.RootObject P in Books)
{
BookList_list.Items.Add(P.volumeInfo.title);
}
}
API Helper task
public class APIHelper
{
private static string BaseURL = "https://www.googleapis.com/books/v1/volumes";
public static async Task<HelperLibrary.Models.GoogleBook.RootObject[] > SearchBooks(string term)
{
using (var WebClient = new HttpClient { BaseAddress = new Uri(BaseURL) })
{
var ResponseHandler = WebClient.GetAsync($"?q= { term } ");
if (ResponseHandler.Result.IsSuccessStatusCode)
{
string x = await ResponseHandler.Result.Content.ReadAsStringAsync();
Debug.WriteLine(x);
Models.GoogleBook.RootObject[] items = JsonConvert.DeserializeObject<Models.GoogleBook.RootObject[]>(await ResponseHandler.Result.Content.ReadAsStringAsync());
return items;
}
else
{
return null;
}
}
}
}
Full error:
JsonSerializationException: Cannot deserialize the current JSON object
(e.g. {"name":"value"}) into type
'System.Collections.Generic.List`1[HelperLibrary.Models.GoogleBook+RootObject]'
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. Path 'kind', line 2, position 8.
I'm already trying to declare it to an array, not a List?
Been trough most other threads about this, can't seem to find a situation similar to mine.
You seem to have misunderstood what is the RootObject. What you have defined as such is actually the inner object inside your RootObject.
I suggest you do this;
1) Rename your RootObject to Item
public class Item
{
public string kind { get; set; }
public string id { get; set; }
public string etag { get; set; }
public string selfLink { get; set; }
public VolumeInfo volumeInfo { get; set; }
public LayerInfo layerInfo { get; set; }
public SaleInfo saleInfo { get; set; }
public AccessInfo accessInfo { get; set; }
}
2) Create a new RootObject
public class RootObject
{
public string kind { get; set; }
public int totalItems { get; set; }
public List<Item> items { get; set; }
}
Then your Deserialize should look like this;
Models.GoogleBook.RootObject root =
JsonConvert.DeserializeObject<Models.GoogleBook.RootObject>(await ResponseHandler.Result.Content.ReadAsStringAsync());
Checking the structure of the json, i think what you are trying to get is the item array of the json if i am not mistaken. What you can do is Try to create a parent class containing the array Root Object something like this:
Public class RootParent
{
public string kind {get; set;}
public int totalItems {get;set;}
public List<RootObject> items {get; set;}
}

Can't deserialize json properly

I have this json:
[{"trace":{"details":{"date":"[28-02-2016 11:04:26.856573]","type":"[info]","message":"[system done.]"},"context":{"context":[[{"ID":"john dillinger"}]]}}},{"trace":{"details":{"date":"[28-02-2016 11:04:26.856728]","type":"[info]","message":"[trace done.]"},"context":{"context":[[{"ID":"john dillinger"}]]}}}]
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type, because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
I've created this class for deserialize it:
public class Testing
{
public string date { get; set; }
public string type { get; set; }
public string message { get; set; }
public string context { get; set; }
}
and this is the code for deserialize the content:
string responseText = "json above";
var obj = JsonConvert.DeserializeObject<Testing>(responseText); //on this line the problem
in the obj line I get the exception. I'm a bit rusty with c# so I don't know exactly what am I doing wrong. Someone could enlighten me?
Your json is not a flat data as your Testing class is. Try using following
public class Details
{
public string date { get; set; }
public string type { get; set; }
public string message { get; set; }
}
public class Context
{
public List<List<ContextElement>> context { get; set; }
}
public class Trace
{
public Details details { get; set; }
public Context context { get; set; }
}
public class RootObject
{
public Trace trace { get; set; }
}
Just hit your json to http://json2csharp.com/ and it seems you need to add this type for the ID part of the context and modify the result so context uses this in the list.
public class ContextElement
{
public string ID { get; set; }
}
Your parsed json is of format
Check this with https://jsonformatter.curiousconcept.com/ yourself. Then you just need to make a C# classes to match that structure.
You need to deserialize a collection of Trace - like List<Trace>:
var obj = JsonConvert.DeserializeObject<List<Trace>>(responseText);
Assuming that you have the following DTOs:
public class Trace
{
public TraceValue trace;
}
public class TraceValue
{
public Details details;
public Context context;
}
public class Details
{
public String date;
public String type;
public String message;
}
public class Context
{
public List<List<IdItem>> context;
}
public class IdItem
{
public String ID;
}
Proof (response is just a line provided by you, but with escaped quotes, so that it can be put directly into the code):
var response =
"[{ \"trace\":{ \"details\":{ \"date\":\"[28-02-2016 11:04:26.856573]\",\"type\":\"[info]\",\"message\":\"[system done.]\"},\"context\":{ \"context\":[[{\"ID\":\"john dillinger\"}]]}}},{\"trace\":{\"details\":{\"date\":\"[28-02-2016 11:04:26.856728]\",\"type\":\"[info]\",\"message\":\"[trace done.]\"},\"context\":{\"context\":[[{\"ID\":\"john dillinger\"}]]}}}]";
var obj = JsonConvert.DeserializeObject<List<Trace>>(response);
I think you should use JavaScripSerializer
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
you can try
var obj = jsSerializer.Deserialize<Testing>(responseText);
I am not sure about this solution, may be it will work or not in your case.
But you can deserialize json string into string array of any dimension as:
var obj = jsSerializer.Deserialize<string[]>(responseText);
var obj = jsSerializer.Deserialize<string[][]>(responseText);

Newtonsoft Json .NET exception thrown

Exception thrown:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Messages.ObjectDescription]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
If someone can figure it out I would appreciate the help. Basicly I instantiate a class called MessageSS, serialize it to a JSON string, then try to deserialize it and the exception gets thrown.
Here are my classes:
[JsonConverter(typeof(StringEnumConverter))]
public enum MessageType
{
req_authenticate,
c_data,
req_state,
c_cmd,
resp_authenticate,
s_cmd,
s_data,
resp_state,
s_state
}
public interface IData { }
public abstract class Message
{
public MessageType type { get; set; }
public virtual string GetJson()
{
return JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
}
public class ObjectDescription
{
public string key { get; set; }
public string type { get; set; }
public double value { get; set; }
public Quality quality { get; set; }
public DateTime timestamp { get; set; }
public ObjectDescription(string _key, string _type, double _value, Quality _quality, DateTime _timestamp)
{
key = _key;
type = _type;
value = _value;
quality = _quality;
timestamp = _timestamp;
}
}
public class MessageSSData : IData
{
public State state { get; set; }
public List<ObjectDescription> data { get; set; }
public MessageSSData(State _state, List<ObjectDescription> _data)
{
state = _state;
this.data = _data;
}
}
public class MessageSS : Message
{
public MessageSSData data { get; set; }
public MessageSS(State state, List<ObjectDescription> data)
{
type = MessageType.s_state;
this.data = new MessageSSData(state, data);
}
}
//Here is the code that throws the exception:
MessageSS mm = new MessageSS(State.CONNECTING, new ObjectDescription[2] { new ObjectDescription("prvi", "tip1", 1.1, Quality.BAD, new DateTime()),
new ObjectDescription("drugi", "tip2", 1.2, Quality.GOOD, new DateTime()) }.ToList());
string json2 = mm.GetJson();
MessageSS mm2 = JsonConvert.DeserializeObject<MessageSS>(json2);
You need to create a default constructor for the type you're deserializing into.
Otherwise, JSON.Net will try to deserialize into your constructor parameters, which don't match your JSON.

Categories

Resources