JSON Deserialization issue with Ziptastic - c#

public class ZiptasticAPIResponse
{
[JsonPropertyName("country")]
public string country { get; set; }
[JsonPropertyName("state")]
public string DefaultState { get; set; }
[JsonPropertyName("city")]
public string DefaultCity { get; set; }
[JsonPropertyName("citiesList")]
public List<CitiesList> CitiesList { get; set; }
}
var url = _alternateURL + zip;
using (var request = new HttpRequestMessage(new HttpMethod("GET"), url))
{
var response = await httpClient.SendAsync(request);
Console.WriteLine("--------------------------");
var content = await response.Content.ReadAsStringAsync();
var ziptasticResponse = JsonConvert.DeserializeObject<ZiptasticAPIResponse>(content);
ziptasticResponse.CitiesList = new List<CitiesList>()
{
new CitiesList()
{
State=ziptasticResponse.DefaultCity,City=ziptasticResponse.DefaultCity
}
};
return ziptasticResponse.CitiesList;
I am getting null for DefaultCity and DefaultState but my country is 'US'
when I enter the url in my browser, I get the entire city, state
https://ziptasticapi.com/90210
What am I missing here?

Your problem is caused by the fact that you are mixing attributes and methods from different serializers. The attribute JsonPropertyName is from System.Text.Json but you are using JsonConvert.DeserializeObject<T>() from Json.NET to deserialize your JSON string. You need to use attributes and methods from the same serializer consistently.
If you want to deserialize using Json.NET, annotate your model with Newtonsoft.Json.JsonPropertyAttribute instead of System.Text.Json.Serialization.JsonPropertyName:
public class ZiptasticAPIResponse
{
[Newtonsoft.Json.JsonProperty("country")]
public string country { get; set; }
[Newtonsoft.Json.JsonProperty("state")]
public string DefaultState { get; set; }
[Newtonsoft.Json.JsonProperty("city")]
public string DefaultCity { get; set; }
[Newtonsoft.Json.JsonProperty("citiesList")]
public List<CitiesList> CitiesList { get; set; }
}
And the later
var ziptasticResponse = JsonConvert.DeserializeObject<ZiptasticAPIResponse>(content);
If you would prefer to use System.Text.Json in .NET 5.0 you may use HttpClientJsonExtensions.GetFromJsonAsync and leave ZiptasticAPIResponse unchanged:
var url = _alternateURL + zip;
try
{
var ziptasticResponse = await httpClient.GetFromJsonAsync<ZiptasticAPIResponse>(url);
return ziptasticResponse;
}
catch (HttpRequestException ex) // Request failed.
{
// Check status code
var statusCode = ex.StatusCode;
Console.WriteLine(statusCode);
}
catch (JsonException) // Malformed JSON
{
Console.WriteLine("Invalid JSON.");
}
catch (NotSupportedException)
{
//https://learn.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-5.0#handle-errors
Console.WriteLine("The content type is not application/json.");
}
// handle other exceptions or let them be handled at some higher level.
No matter which you choose, you might want to avoid mixing using Newtonsoft.Json.* and using System.Text.Json.* using statements in any given file. It's easy to make this mistake as the Newtonsoft and System.Text.Json attributes have very similar, and sometimes identical, names.
Demo fiddle for System.Text.Json here.

Related

how to call API using asp .net

I'm using .net 6 to call API and output data from View
is it possible to call the API using asp mvc
i am generating a url from appsetting.json
{
"AllowedHosts": "*",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ServiceUrl": "http://localhost:8080",
}
then i call the url from the repository and execute it
public class BannerService : IBannerAPI
{
public static readonly string serviceUrl = Setting.ServiceUrl;
//public static readonly string provinceID = Setting.ProvinceID;
public static T CallAPI<T>(string method, Dictionary<string, string> parameters)
{
try
{
var client = new RestClient(serviceUrl);
var request = new RestRequest(method, Method.Get);
if(parameters != null)
{
foreach(var param in parameters )
{
request.AddParameter(param.Key, param.Value);
}
}
var response = client.Execute<T>(request);
return JsonConvert.DeserializeObject<T>(response.Content)!;
}
catch(Exception e) {
LogManager.LogException(e);
return default(T)!;
}
}
public List<BannerModel> GetBanner()
{
return CallAPI<List<BannerModel>>("GetBanner", null);
}
}
It id IBannerAPI
public interface IBannerAPI
{
List<BannerModel> GetBanner();
}
It id BannerModel
public class BannerModel
{
public string BannerID { get; set; }
public string? NgayTao { get; set; }
public string? TieuDeBanner { get; set; }
public string? HinhAnhBanner { get; set; }
public string? STTUuTien { get; set; }
public string HoatDong { get; set; }
}
but i can't pass data to view the old way, i have a sample and it says to use script
how can you help me get API data, if you guys have other way to call API then help me. Please
enter image description here
enter image description here
I can't quite make out whether your issue lies within deserializing your response or in using that response to build a view. If you could be so kind, please provide the raw response and the error message. I'll adjust my answer later, if relevant. I'll make it a bit more generic for now.
I don't have much experience with the RestClient class, my first question is: are you intentionally using a class that runs synchronously instead of asynchronously? If not, then I'd recommend using HttpClient instead as described in this article and implement it as follows:
In Startup.cs
services.AddHttpClient<IBannerClient, BannerClient>()
In BannerService (which I'd recommend you to call BannerClient as it should be separate from your Banner-object business logic if that is relevant)
public class BannerClient : IBannerClient
{
public async Task<List<Banner>> GetBannersAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get, $"{Settings.ServiceUrl}/your/route/here");
var result = new List<Banner>();
try
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await _httpClient.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<List<Banner>>(responseString);
}
catch(Exception ex)
{
// handle error
}
return result;
}
}
If you're set on using RestClient then I'd check the following:
var request = new RestRequest(method, Method.Get);
I think calling this the method is a bit confusing, rather call it route or uri. Can you confirm that the url on the rest client is set correctly? (Small note: you don't have to set the actual method to Get as that seems to be the default)
Also, what drew my attention:
return JsonConvert.DeserializeObject<T>(response.Content)!;
I see an exclamation mark after this statement, what does that do?
EDIT:
Based on the response you posted, it seems like your datamodel doesn't match the JSON coming back which leads to an error in deserialization. The JSON Deserializer matches based on best-effort (can be manipulated by attributes as I'll show you) so it tries to find a property called "data" to bind to, but doesn't find it in your model. You should structure you class like so:
using Newtonsoft.Json;
using System.Collections.Generic;
namespace Exact.Office365.TeamsApp.Model
{
public class BannersReponse
{
// Here's a way to influence the property it tries to bind to
[JsonProperty("data")]
public List<Banner> Banners { get; set; }
}
public class Banner
{
public bool HoatDong { get; set; }
public string NgayTao { get; set; }
public string STTUuTien { get; set; }
public string BannerID {get; set; }
public string TieuDeBanner { get; set; }
public string HinhAnhBanner {get; set; }
}
}

JsonConvert.DeserializeObject returns nulls

I have the following code which I am using in an ASP.NET MVC c# application
HttpClient client = new HttpClient();
try
{
var result = client.GetStringAsync(url).Result;
APIReturn = JsonConvert.DeserializeObject<APIReturn>(result);
}
catch
{
}
When I place my breakpoint on the APIReturn = .... line, and I view the contents of result, I see what looks like a valid return from the API call. I even copied the contents of the variable result and applied it to an online json tool. This is what the tool shows:
This is the definition of the APIReturn class:
public class APIReturn
{
public string return_response { get; set; }
public string return_code { get; set; }
public string return_plan_name { get; set; }
public string return_menu_string { get; set; }
public string return_peo_ind { get; set; }
}
At the end of the execution of the code, I look at the values of APIReturn and each field is null.
Any ideas why Json is not parsing the string?
Thank you.
Your json is an array of objects... but you are deserializing it to an object. Change the deserialization to List and should work
var list = JsonConvert.DeserializeObject<List<APIReturn>>(result);

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

Unable to use JSON data with Newtonsoft.Json in WPF

I am retrieving data from office365 api. The response is in JSON format. I want to get data like Id, DisplayName etc. into variables but not getting the right way to do it. Following this link. I'm new to API and JSON. Will Appreciate pointers as well towards best learning links.Sample JSON below for listing sub folders of Inbox folder.
Response JSON data.
{"#odata.context":"https://outlook.office365.com/api/v1.0/$metadata#Me/Folders('Inbox')/ChildFolders","value":
[
{"#odata.id":"https://outlook.office365.com/api/v1.0/Users('sample.user#demosite.com')/Folders('AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1iMjllLTJmsdfsdfdDSFSDFDFDF=')",
"Id":"AAMkADBjMdfgdfgDFGDFGDFGdfGDFGDFGDFGGDzrACAAB4xqMmAAA=",
"DisplayName":"SampleFolder","ParentFolderId":"AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1sdsDFSDFSDFSDFSDFSDFDFDFrACAAAAAAEMAAA=","ChildFolderCount":0,"UnreadItemCount":8,"TotalItemCount":94},
{"#odata.id":"https://outlook.office365.com/api/v1.0/Users('sample.user#demosite.com')/Folders('AAMkADBjMGZiZGFlLTE4ZmEasdasdasdASDASDASDASDSADDASDASDAB4xqMnAAA=')",
"Id":"AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1iMjllLTJmOGZkNGRhZmIzNQAuAasdASDASDASDASEDASDASDxSEHjzrACAAB4xqMnAAA=",
"DisplayName":"AnotherSampleFolder","ParentFolderId":"AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1sdsDFSDFSDFSDFSDFSDFDFDFrACAAAAAAEMAAA=","ChildFolderCount":0,"UnreadItemCount":21,"TotalItemCount":75}
]
}
The C# code using to parse JSON and find the required data.
HttpResponseMessage response = httpClient.SendAsync(request).Result;
if (!response.IsSuccessStatusCode)
throw new WebException(response.StatusCode.ToString() + ": " + response.ReasonPhrase);
string content = response.Content.ReadAsStringAsync().Result;
JObject jResult = JObject.Parse(content);
if (jResult["odata.error"] != null)
throw new Exception((string)jResult["odata.error"]["message"]["value"]);
//Attempt one - using dynamic [NOT WORKING - getting NULL values in the variables]
dynamic results = JsonConvert.DeserializeObject<dynamic>(content);
var folderName = results.Id;
var folderId = results.Name;
//Attempt two - [Not working - Throwing exception -
//Object reference not set to an instance of an object.]
var folderID = (string)jResult["odata.context"]["odata.id"][0]["Id"];
First create a class for your json object
public class RootObject
{
[JsonProperty(PropertyName = "#odata.context")]
public string context { get; set; }
public List<Value> value { get; set; }
}
public class Value
{
[JsonProperty(PropertyName = "#odata.id")]
public string dataId { get; set; }
public string Id { get; set; }
public string DisplayName { get; set; }
public string ParentFolderId { get; set; }
public int ChildFolderCount { get; set; }
public int UnreadItemCount { get; set; }
public int TotalItemCount { get; set; }
}
Then Json Convert the Json string to your RootObject if your are using Newtonsoft Json then Deserilaze by using
RootObject shortiee = JsonConvert.DeserializeObject<RootObject>("Your Json String");
private List<string> GetDisplayNames(JObject content)
{
var obj = Json.Parse(content);
var values = obj["value"].ToList();
var displayNames = new List<string>();
foreach (var value in values)
{
displayNames .Add(system["DisplayName"].ToString());
}
return displayNames;
}
This would return the names, for example, and you could do this for each value you need to retrieve. However, this does not require you to serialize/deserialize the json object before using it. It works, but is most likely not best practice.
if (jResult["odata.error"] != null)
throw new Exception((string)jResult["odata.error"]["message"]["value"]);
//Attempt one - using dynamic [NOT WORKING - getting NULL values in the variables]
dynamic results = JsonConvert.DeserializeObject<dynamic>(content);
Side note: There is no key called "odata.error" in your JSON data. So you're effectively calling something which will return null.
One of the ways to deserialise JSON is to create model classes for the objects you want to process and deserialise into them directly, eg. JsonConvert.DeserializeObject<Folder>(content). As you are talking to an Office365 API, you find documentation and examples here on how they are defined.
Taken your folder response as an example, your model for a single Folder could look like this:
public class Folder
{
[JsonProperty(PropertyName = "#odata.id")]
public string OdataId { get; set; }
public string Id { get; set; }
public string DisplayName { get; set; }
public string ParentFolderId { get; set; }
public int ChildFolderCount { get; set; }
public int UnreadItemCount { get; set; }
public int TotalItemCount { get; set; }
}
Note1: in your example, you get a response with list of folders, so have to adjust this accordingly.
Note2: you can use JsonProperty to define a mapping between a JSON property/key and a C# property as shwon for #odata.id.
However, you can also use the Outlook Client Library which would make it mostly unnecessary to deal with JSON data directly (which seems preferable, unless you have a very specific reason to deal with JSON directly).

Caching and Parsing etag with HttpClient and Odatav4(Etag always null in Httpclient)

The case : I'm Using EF6 and WebApi controls to store my result
public class Profile
{
public string ZipFile { get; set; } // input parameter
[ConcurrencyCheck]
public Guid Id { get; set; } // output parameter generated by Web API
public string Name { get; set; } // output parameter from Profile XML file
public string Description { get; set; } // output parameter from Profile XML file}
So i'm using ConcurrencyCheck to generate etag with odataV4
When i'm reading the get result with post man i getting :
As you see the etage there
The problem : in my client i use HttpClient as follows
public static string GetAsJsonAsync(string route)
{
string result = string.Empty;
try
{
var fullRoute = PrepareHttpRequest(route);
var client = new HttpClient();
using (HttpResponseMessage response = client.GetAsync(fullRoute).Result)
{
result = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
}
}
catch (Exception ex)
{
throw new HttpRequestException(result, ex);
}
return result;
}
the response header if-match and etag are always ,I have no idea how to parse the "#odata.etg" value,
so my question is how i parsing the etag value and store it?
For future references the solution is Attach to your model [JsonProperty("#odata.etag")]

Categories

Resources