How to structure my models to match my JSON? - c#

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;
}

Related

400 (Bad Request) while sending json to .net 6 Web API

I am trying to send data from winform datagridview to webapi, so i convert datagridview to json. While sending json it failed to POST data and send this error:
'Failed to POST data: (BadRequest): {"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title": "One or more validation errors occurred.","status":400,"traceId":"00-fb84f3c1c1802a481cbe7aba7ef73193-80be6fe9c57344d2-00","errors":{"$":["The JSON value could not be converted to LabDataApi.Models.LabData. Path: $ | LineNumber: 0 | BytePositionInLine: 421."]}}'
Codes:
public class LabData
{
public int SerialNumber { get; set; }
public string LabParameterName { get; set; }
public decimal LabValue { get; set; }
public DateTime LabDate { get; set; }
}
private void button2_Click(object sender, EventArgs e)
{
var url = "https://localhost:7248/api/Lab";
dataGridView1.DataSource = LabResult.GetLabData();
var table = JsonConvert.SerializeObject(dataGridView1.DataSource);
ApiSender apiSender = new ApiSender();
apiSender.POSTData(table, url);
}
public class ApiSender
{
private static HttpClient _httpClient = new HttpClient();
public bool POSTData(object json, string url)
{
using (var content = new StringContent(JsonConvert.SerializeObject(json), System.Text.Encoding.UTF8, "application/json"))
{
HttpResponseMessage result = _httpClient.PostAsync(url, content).Result;
if (result.StatusCode == System.Net.HttpStatusCode.Created)
return true;
string returnValue = result.Content.ReadAsStringAsync().Result;
throw new Exception($"Failed to POST data: ({result.StatusCode}): {returnValue}");
}
}
}
Receiving Json data format
[{"SerialNumber":1,"LabParameterName":"abc","LabValue":7.80,"LabDate":"2001-11-16T10:10:00"},{"SerialNumber":2,"LabParameterName":"xyz","LabValue":10.00,"LabDate":"2001-11-16T10:10:00"},{"SerialNumber":3,"LabParameterName":"qq","LabValue":5.00,"LabDate":"2001-03-16T10:10:00"},{"SerialNumber":18,"LabParameterName":"cbc","LabValue":200.0,"LabDate":"2001-11-16T10:10:00"}]
The JSON value could not be converted to LabDataApi.Models.LabData
Well, you are trying to convert an array of LabData into the LabData model. Update the API to receive IEnumerable<LabData>, or update the client to send multiple POST requests (for each LabData entry (not recommended)).
At least that's the answer to the question you've asked - it looks like you have cut off the end of the exception, perhaps you omitted some essential details by mistake?
There is an issue with your objects definitions when converting to json. Also, you'll want to get it as a List or an Array.
Your serialization/deserialization will work better with these objects.
public class LabDatas
{
public LabData[] Value { get; set; }
}
//or if you want list
public class LabDatas
{
public List<LabData> Value { get; set; }
}
public class LabData
{
public int SerialNumber { get; set; }
public string LabParameterName { get; set; }
public float LabValue { get; set; }
public DateTime LabDate { get; set; }
}

Call and get response from web api in a winform c# application

I am making a simple WinForm Application in Windows and I want to get some data about foreign exchange rates. So I decided to call an API from Oanda. I tried several things around but nothing worked. It gives the response in CSV as well as JSON format. I don't know which will be easier to handle.
Also for this type of response, I am unable to create its model class.
Response:
JSON:
{
"meta": {
"effective_params": {
"data_set": "OANDA",
"base_currencies": [
"EUR"
],
"quote_currencies": [
"USD"
]
},
"endpoint": "spot",
"request_time": "2019-06-08T12:05:23+00:00",
"skipped_currency_pairs": []
},
"quotes": [
{
"base_currency": "EUR",
"quote_currency": "USD",
"bid": "1.13287",
"ask": "1.13384",
"midpoint": "1.13336"
}
]
}
CSV:
base_currency,quote_currency,bid,ask,midpoint
EUR,USD,1.13287,1.13384,1.13336
I just need those three numbers so, which method will be helpful and how.
This code I already tried:
var client = new HttpClient();
client.BaseAddress = new Uri("https://www1.oanda.com/rates/api/v2/rates/");
HttpResponseMessage response = await client.GetAsync("spot.csv?api_key=<myapikey>&base=EUR&quote=USD");
string result = await response.Content.ReadAsStringAsync();
textBox1.Text = result;
Edit: I need the result of this call for my further processing so I must need this method to complete its execution before proceeding further
First creating model from Json:
use a online model generator like Json2C#, for the Json that you have posted, following is the model generated:
public class EffectiveParams
{
public string data_set { get; set; }
public List<string> base_currencies { get; set; }
public List<string> quote_currencies { get; set; }
}
public class Meta
{
public EffectiveParams effective_params { get; set; }
public string endpoint { get; set; }
public DateTime request_time { get; set; }
public List<object> skipped_currency_pairs { get; set; }
}
public class Quote
{
public string base_currency { get; set; }
public string quote_currency { get; set; }
public string bid { get; set; }
public string ask { get; set; }
public string midpoint { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<Quote> quotes { get; set; }
}
Now connecting to the WebAPI using HttpClient, which has the option to return both Json and CSV, I would prefer JSON being standard, which can also be consumed easily by variety of clients, use the following simple generic methods:
Assuming it is GET only call, just supply the Host and API details to the generic Process method underneath:
public async Task<TResponse> Process<TResponse>(string host,string api)
{
// Execute Api call Async
var httpResponseMessage = await MakeApiCall(host,api);
// Process Json string result to fetch final deserialized model
return await FetchResult<TResponse>(httpResponseMessage);
}
public async Task<HttpResponseMessage> MakeApiCall(string host,string api)
{
// Create HttpClient
var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Make an API call and receive HttpResponseMessage
HttpResponseMessage responseMessage = await client.GetAsync(api, HttpCompletionOption.ResponseContentRead);
return responseMessage;
}
public async Task<T> FetchResult<T>(HttpResponseMessage result)
{
if (result.IsSuccessStatusCode)
{
// Convert the HttpResponseMessage to string
var resultArray = await result.Content.ReadAsStringAsync();
// Json.Net Deserialization
var final = JsonConvert.DeserializeObject<T>(resultArray);
return final;
}
return default(T);
}
How to use:
Simply call:
var rootObj = await Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
You receive the deserialized RootObject as shown in the model above
For anything further complex processing like sending input to the call with http body, above generic code needs further modification, it is currently only specific to your requirement
Edit 1: (Making the entry call Synchronous)
To make the overall call synchronous, use the GetAwaiter().GetResult() at the topmost level, Main method will be converted to, rest all will remain same as in the sample (async methods)
void Main()
{
var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/").GetAwaiter().GetResult();
}
Edit 2: (Making complete code Synchronous)
void Main()
{
var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
}
public TResponse Process<TResponse>(string host, string api)
{
// Execute Api call
var httpResponseMessage = MakeApiCall(host, api);
// Process Json string result to fetch final deserialized model
return FetchResult<TResponse>(httpResponseMessage);
}
public HttpResponseMessage MakeApiCall(string host, string api)
{
// Create HttpClient
var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Make an API call and receive HttpResponseMessage
HttpResponseMessage responseMessage = client.GetAsync(api, HttpCompletionOption.ResponseContentRead).GetAwaiter().GetResult();
return responseMessage;
}
public T FetchResult<T>(HttpResponseMessage result)
{
if (result.IsSuccessStatusCode)
{
// Convert the HttpResponseMessage to string
var resultArray = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
// Json.Net Deserialization
var final = JsonConvert.DeserializeObject<T>(resultArray);
return final;
}
return default(T);
}
You can use an online service such as json2csharp to get your json model and Json.Net to serialise and deserialise the json string. The following models your json.
public class EffectiveParams
{
public string data_set { get; set; }
public List<string> base_currencies { get; set; }
public List<string> quote_currencies { get; set; }
}
public class Meta
{
public EffectiveParams effective_params { get; set; }
public string endpoint { get; set; }
public DateTime request_time { get; set; }
public List<object> skipped_currency_pairs { get; set; }
}
public class Quote
{
public string base_currency { get; set; }
public string quote_currency { get; set; }
public string bid { get; set; }
public string ask { get; set; }
public string midpoint { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<Quote> quotes { get; set; }
}
Note that you can change RootOject to a more descriptive name.
So, for example, to get the bid, ask and midpoint value for each quotes, you can simply do this:
RootObject rootObj=JsonConvert.DeserializeObject(jsonString);
//Get the required values.
foreach(var quote in rootObj.quotes)
{
Console.WriteLine($"Bid : {quote.bid} Ask: {quote.ask} MidPoint: {quote.midpoint}");
}

Accessing a JSON object inside HttpResponseMessge using StringContent

I have a problem accessing a JSON object inside a HttpResponseMessage that is made using StringContent.
Here's what the JSON Objects looks like:
{
"object": "JOB",
"entry": [
{
"uuid": "1nufisnfiu3-dn3u2irb-dn3ui2fb9-nui2",
"changed_fields": [
"status"
],
"time": "2018-09-30 21:57:02"
}
],
"resource_url": "https://somewebsiteAPI.com/api_1.0/Jobs/1nufisnfiu3-dn3u2irb-dn3ui2fb9-nui2.json"
}
Here's my Controller:
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
string result = await Request.Content.ReadAsStringAsync();
var resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StringContent(result, System.Text.Encoding.UTF8, "application/json");
return resp;
}
My objective here is to get the resource_url json object.
Any help would be so much appreciated! Thanks!
You need to let JSON string to be object, then get resource_url value.
I would use Json.net to make it.
Create your model for Json data.
look like this.
public class Entry
{
public string uuid { get; set; }
public List<string> changed_fields { get; set; }
public string time { get; set; }
}
public class RootObject
{
public List<Entry> entry { get; set; }
public string resource_url { get; set; }
}
use JsonConvert.DeserializeObject method let JSON string to model.
then get the model resource_url property and pass as parameter in StringContent.
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
string result = await Request.Content.ReadAsStringAsync();
var resp = new HttpResponseMessage(HttpStatusCode.OK);
var model = JsonConvert.DeserializeObject<RootObject>(result);
resp.Content = new StringContent(model.resource_url, System.Text.Encoding.UTF8, "application/json");
return resp;
}
Should really let the model binder do its intended roles instead of trying to parse the JSON yourself.
[HttpPost]
public async Task<IHttpActionResult> Post([FromBody]Job model) {
if(ModelState.IsValid) {
//...do something with model
return Ok(model.ResourceUrl);
}
return BadRequest(ModelState);
}
Where Job is defined from the provided JSON
public partial class Job {
[JsonProperty("object")]
public string Object { get; set; }
[JsonProperty("entry")]
public Entry[] Entry { get; set; }
[JsonProperty("resource_url")]
public Uri ResourceUrl { get; set; }
}
public partial class Entry {
[JsonProperty("uuid")]
public string Uuid { get; set; }
[JsonProperty("changed_fields")]
public string[] ChangedFields { get; set; }
[JsonProperty("time")]
public DateTimeOffset Time { get; set; }
}
The model binder would parse the incoming JSON and populate the strongly typed object model defined to match the desired data.

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);

Filling list with Json data and getting the objects from it

I have a php webservice which gets the data from database and returns it as json data.
Json data
{"faqs":
[
{"faq":{"id":"123"}},
{"faq":{"id":"124"}}
]
}
Object classes
public class FaqList
{
public List<Faq> faqs { get; set; }
}
public class Faq
{
public string id { get; set; }
}
Test class
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(new Uri("http://www.mydomain.com/webservice/7/server.php"));
var jsonString = await response.Content.ReadAsStringAsync();
FaqList list = JsonConvert.DeserializeObject<FaqList>(jsonString);
list.faqs.Count() => 2
list.faqs[0].id => NULL !!
I fill all the objects to the 'list'. With count test I see that it's filled. But if I try to get an objects from it, I get null error.
So, how can I correctly fill my list so that I can get the objects from it?
http://json2csharp.com/ suggests these definitions. (One more class Faq2)
public class Faq2
{
public string id { get; set; }
}
public class Faq
{
public Faq2 faq { get; set; }
}
public class RootObject
{
public List<Faq> faqs { get; set; }
}

Categories

Resources