Deserialize object in ASP.NET Core 6 - c#

Please help me to solve this issue. I am try to call DeserializeObject using JsonConvert, but it's returning an empty result to my view. My API response is fine and receives data, but I cannot deserialize it into a company model.
This is my code:
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string apiResponse = await response.Content.ReadAsStringAsync();
company = JsonConvert.DeserializeObject<company>(apiResponse);
}
My company model is:
public class company
{
public string bankAccount { get; set; } = string.Empty;
public string bankName { get; set; } = string.Empty;
public string cif { get; set; } = string.Empty;
public string creationDate { get; set; } = string.Empty;
public string email { get; set; } = string.Empty;
public string fiscalAttribute { get; set; } = string.Empty;
public string id { get; set; } = string.Empty;
public string locationAddress { get; set; } = string.Empty;
public string locationCounty { get; set; } = string.Empty;
public string name { get; set; } = string.Empty;
public string nrRegCom { get; set; } = string.Empty;
public string passwordHash { get; set; } = string.Empty;
public string phoneNumber { get; set; } = string.Empty;
public string privilegeKey { get; set; } = string.Empty;
public string registrationState { get; set; } = string.Empty;
public string storedHardwareID { get; set; } = string.Empty;
public string subscriptionExpirationDate { get; set; } = string.Empty;
public string subscriptionID { get; set; } = string.Empty;
public string actionConfirmationID { get; set; } = string.Empty;
public string updateDate { get; set; } = string.Empty;
}
The API call is returning a result in JSON format:
{
"company": {
"bankAccount": "777",
"bankName": "777",
"cif": "123",
"creationDate": "2023-01-16T17:38:45.962Z",
"email": "pankaj#gmail.com",
"fiscalAttribute": "123",
"id": "f0e3bfef5bcf40c987713153e55dceef",
"locationAddress": "india",
"locationCounty": "india",
"name": "pankaj",
"nrRegCom": "123",
"passwordHash": "7c78eea7a591b0c8a4dad680372e35ca12e11cffdac5c69a39700c8014fbcc82",
"phoneNumber": "88888858",
"privilegeKey": "",
"registrationState": 1,
"storedHardwareID": "",
"subscriptionExpirationDate": "0001-01-01T00:00:00Z",
"subscriptionID": "",
"actionConfirmationID": "",
"updateDate": "2023-01-16T17:38:45.962Z"
}
}

The problem is that your returned JSON is an object that has a key of "company" on the root level. Imagine the outer {} as an unnamed object which contains the property "Company", which is an object with its own properties.
Now you have 2 different options:
1:
Create a class that acts as a wrapper around the company to reflect the JSON structure on your .NET objects correctly, e.g.
public class CompanyWrapper
{
public Company company { get; set; }
}
Then deserialize your JSON result onto this wrapper, it should work now.
However this should be avoided in this case because the wrapper itself provides absolutely no other value to your use-case besides, well, acting as a wrapper.
2:
Another way would be to refactor your API call result, if you have access to the API to return the root {} object without the additional, useless "company"-key on the root level. If you don't have access and don't want to touch the JSON result otherwise, then use the wrapper approach.
Also keep in mind that the standard naming convention for classes and properties in C# is Pascal-Case, which means that the first letter of a given name should be uppercase.

Related

deserialize API response with different json value in the result

I'm querying an external service and wanted to deserialize the response into a customer object but the issue is response for each customer may be different. some customer may have Sales entity in the response and few may have Marketing.
The json property for sales entity is SalesId and for marketing is MarketingId. Can you advise whether the model I use to store result is correct or any improvement ? If so, how would I deserialize the response without knowing the correct json property ?
For Customer 66666
{
"customerId": "66666",
"customerName": "test1234",
"dependentEntity": [
{
"SalesId": "3433434",
"SalesPersonName": "343434",
"SaleSource": "StorePurchase"
}
]
}
For Customer 5555
{
"customerId": "55555",
"customerName": "test2",
"dependentEntity": [
{
"MarketingId": "3433434",
"MarketingAppName": "343434",
"MarketingSource": "Online"
}
]
}
Here is the Model I'm thinking but not sure the correct one
public class Customer
{
public string customerId { get; set; }
public string customerName { get; set; }
public IList<T> dependentList { get; set; }
}
public class Dependent
{
[JsonProperty("Id")]
public string Id { get; set; }
public string Name { get; set; }
public string Source { get; set; }
}
You could probably try something like the following one:
public class DependentEntity
{
[JsonProperty("SalesId")]
public string SalesId { get; set; }
[JsonProperty("SalesPersonName")]
public string SalesPersonName { get; set; }
[JsonProperty("SaleSource")]
public string SaleSource { get; set; }
[JsonProperty("MarketingId")]
public string MarketingId { get; set; }
[JsonProperty("MarketingAppName")]
public string MarketingAppName { get; set; }
[JsonProperty("MarketingSource")]
public string MarketingSource { get; set; }
}
public class Customer
{
[JsonProperty("customerId")]
public string CustomerId { get; set; }
[JsonProperty("customerName")]
public string CustomerName { get; set; }
[JsonProperty("dependentEntity")]
public IList<DependentEntity> DependentEntity { get; set; }
}
We have a type for DependentEntity that has both the attributes of Marketing and Sales object. After parsing your input, you could create a logic (checking the attributes) based on which you could check if a DependentEntity is a Marketing or a Sales object.
The above classes was generated using, jsonutils.
If we can assume that the dependentEntity contains only a single type of objects then you can use json.net's schema to perform branching based on the matching schema.
So, lets suppose you have these dependent entity definitions:
public class DependentMarket
{
public string MarketingId { get; set; }
public string MarketingAppName { get; set; }
public string MarketingSource { get; set; }
}
public class DependentSales
{
public string SalesId { get; set; }
public string SalesPersonName { get; set; }
[JsonProperty("SaleSource")]
public string SalesSource { get; set; }
}
...
Then you can use these classes to generate json schemas dynamically:
private static JSchema marketSchema;
private static JSchema salesSchema;
//...
var generator = new JSchemaGenerator();
marketSchema = generator.Generate(typeof(DependentMarket));
salesSchema = generator.Generate(typeof(DependentSales));
And finally you can do the branching like this:
var json = "...";
var semiParsedJson = JObject.Parse(json);
JArray dependentEntities = (JArray)semiParsedJson["dependentEntity"];
JObject probeEntity = (JObject)dependentEntities.First();
if (probeEntity.IsValid(marketSchema))
{
var marketEntities = dependentEntities.ToObject<List<DependentMarket>>();
...
}
else if (probeEntity.IsValid(salesSchema))
{
var salesEntities = dependentEntities.ToObject<List<DependentSales>>();
...
}
else if ...
else
{
throw new NotSupportedException("The provided json format is not supported");
}

Calling an Api multiple times C#

I am trying to get use the rawg api and get games from their api, i have created the game entity class to parse the data to
//game entity
public class Game
{
public int Id { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public string Released { get; set; }
public string Background_Image { get; set; }
public int Rating { get; set; }
public int Playtime { get; set; }
}
now i need to get the games from the api, and i made this method
public static async Task<List<Game>> ApiRawgGamesRequest()
{
var gamesList = new List<Game>();
for (int i = 1; i < 250; i++)
{
using (var msg = new HttpRequestMessage(HttpMethod.Get, new Uri($"https://api.rawg.io/api/games?page_size=40&page={i}")))
using (var response = await _client.SendAsync(msg))
{
response.EnsureSuccessStatusCode();
var gamesResponse = await response.Content.ReadAsAsync<Game[]>();
gamesList.AddRange(gamesResponse);
}
}
return gamesList;
}
i am trying not to add all the games at once so i used a for loop to continously add the games into a list to potentially reduce the stress.
but i get an error
// error
Unhandled exception. System.AggregateException: One or more errors occurred. (Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'Domain.Game[]' because the t
ype 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<T>) 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 'count', line 1, position 9.)
the response from the rawg api looks like this -- the results part is the game data i am trying to collect
// api response
{
"count": 454988,
"next": "https://api.rawg.io/api/games?page=2&page_size=40",
"previous": null,
"results": [
{
"id": 3498,
"slug": "grand-theft-auto-v",
"name": "Grand Theft Auto V",
"released": "2013-09-17",
"tba": false,
"background_image": "https://media.rawg.io/media/games/84d/84da2ac3fdfc6507807a1808595afb12.jpg",
"rating": 4.48,
},
{
"id": 4200,
"slug": "portal-2",
"name": "Portal 2",
"released": "2011-04-18",
"tba": false,
"background_image": "https://media.rawg.io/media/games/328/3283617cb7d75d67257fc58339188742.jpg",
"rating": 4.62
}
]
}
is there a better way to do this?
Your exception is resulting from a wrong JSON parsed object.
In your code you are calling:
var gamesResponse = await response.Content.ReadAsAsync<Game[]>();
But the response is the object (from your example JSON):
public class GameResponse
{
public int Count { get; set; }
public string Next{ get; set; }
public string Previous { get; set; }
public IEnumerable<Game> Results { get; set; }
}
If you change it to:
var gamesResponse = await response.Content.ReadAsAsync<GameResponse>();
You will be successful in parsing the response and access the games via:
gamesResponse.Results
So, you are trying to deserialize only the "results" part of the json, you have to build a class with all the items from the response json.
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class Result {
public int id { get; set; }
public string slug { get; set; }
public string name { get; set; }
public string released { get; set; }
public bool tba { get; set; }
public string background_image { get; set; }
public double rating { get; set; }
}
public class Root {
public int count { get; set; }
public string next { get; set; }
public object previous { get; set; }
public List<Result> results { get; set; }
}
You can always use some kind of Json>class generator online, used this for this anwser: https://json2csharp.com/
There are a couple of good answers addressing the issue of not having the correct structure. The only thing I am adding here as a good practice and for better control of your variable names are the annotations. Note, DataContract and DataMember are part of the framework but work well with Json.Net even though Json.Net comes with their own set of annotations.
[DataContract]
public class Payload
{
[DataMember(Name = "count")]
public int Count { get; set; }
[DataMember(Name = "next")]
public string Next { get; set; }
[DataMember(Name = "previous")]
public string Previous { get; set; }
[DataMember(Name = "results")]
public List<GameData> Results { get; set; }
}
[DataContract]
public class GameData
{
[DataMember(Name = "id")]
public long Id { get; set; }
[DataMember(Name = "slug")]
public string Slug { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "rating")]
public float Rating { get; set; }
[DataMember(Name = "rating_top")]
public float Rating_top { get; set; }
}
public static async Task<IEnumerable<Payload>> GetGameDatasAsync(int page = 1, int pageSize = 40, bool followNext = false, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
string next = $"https://api.rawg.io/api/games?page_size={pageSize}&page={page}";
var results = new List<Payload>();
using (var client = new HttpClient())
do
{
var result = await client.GetAsync(next, cancellationToken);
next = string.Empty;
if (result.IsSuccessStatusCode)
{
var json = await result.Content.ReadAsStringAsync();
if (!string.IsNullOrWhiteSpace(json))
{
var payload = JsonConvert.DeserializeObject<Payload>(json);
next = payload.Next;
results.Add(payload);
}
}
} while (!string.IsNullOrWhiteSpace(next) && followNext);
return results;
}

retrive specific data from nested json string

I have a block of JSON as follows:
{
"FirstName": "JON",
"LastName": "BAYN",
"Data": [
{
"Plan": "DAY"
}
]
}
I have built it using JavaScriptSerializer like
JavaScriptSerializer serializer_user = new JavaScriptSerializer();
dynamic jsonObject = serializer_user.Deserialize<dynamic>(content_);
dynamic firstname = jsonObject["FirstName"];
firstname = jsonObject["FirstName"];
But I am not able to read from nested "Details" >> "Plan". I've been unable to piece together how to accomplish this goal.
At first, make model class to your json schema:
public class Rootobject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public int MemberID { get; set; }
public Detail[] Details { get; set; }
}
public class Detail
{
public string Plan { get; set; }
public string Product { get; set; }
public DateTime ProductStartDate { get; set; }
public DateTime ProductEndDate { get; set; }
public string Flag { get; set; }
}
Now you can deserialize your json string to RootObject (Use Json.NET instead of JavaScriptSerializer because it is faster etc):
using Newtonsoft.Json;
..
// If Json.NET is not option:
// var obj = new JavaScriptSerializer().Deserialize<Rootobject>(json)
var obj = JsonConvert.DeserializeObject<Rootobject>(json);
And now you are able to access object structure like following:
if (obj.Details != null)
{
foreach (var detail in obj.Details)
{
Console.WriteLine(detail.Plan);
}
}
If you don't want to create new classes for it and deserialize it, you could just do a regex.

C# Entity Framework Json deserialize String array issues

I have A Json file Which can be used for deserialize to Entity framework. For simplify we can assume the Json like this
{
"stat": "val0",
"results": [
{
"datasets": [
"val1",
"val2"
],
"head": "val3"
},
{
"datasets": [
"val4",
"val5"
],
"head": "val6"
}
]
}
And my Entity Classes like
[Serializable]
public class Root
{
[Key]
public int Id { get; set; }
public int stat { get; set; }
public List<Result> results { get; set; }
}
[Serializable]
public class Result
{
[Key]
public int Id { get; set; }
public List<String> _strings { get; set; }
public List<string> Strings
{
get { return _strings; }
set { _strings = value; }
}
[Required]
public string datasets
{
get { return String.Join(",", _strings); }
set { _strings = value.Split(',').ToList(); }
}
public string head{ get; set; }
public virtual root { get; set; }
}
I know Entity Framework does not support primitive types and I know problem causes from my datasets fields. that I found this way to solve String array deserialize issue here. I have tried
URL = "http://...";//Restful webservice address
WebClient client = new WebClient();
String JSON= client.DownloadString(URL);
var dsobj = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<RootObject>(json);
But I got
System.InvalidOperationException
Then I have decided to use Newtonsoft
URL = "http://...";//Restful webservice address
WebClient client = new WebClient();
String JSON= client.DownloadString(URL);
var dsobj = JsonConvert.DeserializeObject<Root>(json);
Then I got this error
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: [. Path 'results[0].senses[0].definition', line 1, position...
I found this but I cant figure it out.
How can Fix these isseus. Any help appreciated.
Your json consist of two unwanted commas, try removing those
try
[Serializable]
public class Root
{
[Key]
public int Id { get; set; }
public string stat { get; set; } // changed to a string
public List<Result> results { get; set; }
}
[Serializable]
public class Result
{
[Key]
public int Id { get; set; }
public List<String> _dataSets { get; set; }
public List<string> dataSets // the JSON array will deserialize into this property
{
get { return _dataSets; }
set { _dataSets = value; }
}
[Required]
public string DatasetsAsString
{
get { return String.Join(",", _dataSets); }
set { _dataSets = value.Split(',').ToList(); }
}
public string head{ get; set; }
public virtual root { get; set; }
}
Edit: stat property has to be a string too.

Trying to consume SmartyStreets JSON with JSON.Net... "Cannot deserialize JSON array into type Components"

I'm trying to consume SmartyStreets JSON LiveAddress API and I'm having some difficulties. I will admit that I'm not too familiar with JSON. Anyways, I've tried a few different methods and I usually end up with the error "Cannot deserialize JSON array into type Metadata".
Here is the JSON string:
[{"input_index":0,"candidate_index":0,"delivery_line_1":"1600 Amphitheatre Pkwy","last_line":"Mountain View CA 94043-1351","delivery_point_barcode":"940431351000","components":{"primary_number":"1600","street_name":"Amphitheatre","street_suffix":"Pkwy","city_name":"Mountain View","state_abbreviation":"CA","zipcode":"94043","plus4_code":"1351","delivery_point":"00","delivery_point_check_digit":"0"},"metadata":{"record_type":"S","county_fips":"06085","county_name":"Santa Clara","carrier_route":"C058","congressional_district":"14"},"analysis":{"dpv_match_code":"Y","dpv_footnotes":"AABB","dpv_cmra":"N","dpv_vacant":"N","ews_match":false,"footnotes":"N#"}}]
I used the jsontocsharp webapp to create classes.
Here is the code I'm using:
using (var webClient = new WebClient())
{
var json = webClient.DownloadString("url");
var md = JsonConvert.DeserializeObject<Metadata>(json);
litTest.Text = md.county_name;
}
Which then throws the error I mentioned above.
Any assistance would be greatly appreciated.
Thank you,
Andrew
I'm a developer at SmartyStreets--thanks for using our service!
The main thing you should understand is that the JSON response is an array of address objects, not just one. This is because an address may be ambiguous, requiring selection/confirmation by the consumer.
So that means you need to tell Json.Net to deserialize the JSON as the top-level address object and then traverse into each address to get the metadata (you were trying to parse the metadata directly, which doesn't work because there's one metadata section for each address in the array returned). That's basically what L.B is doing in his answer, except he's added some extra overhead in order to use dynamic.
Here's an alternate solution that uses the same "DeserializeObject" from your question:
namespace JsonFun
{
using System;
using System.Net;
using Newtonsoft.Json;
class Program
{
private const string Url = "https://api.qualifiedaddress.com/street-address/?street=1600%20Amphitheatre%20Parkway&street2=&city=Mountain%20View&state=CA&zipcode=94043&candidates=10&auth-token=YOUR_AUTH_TOKEN_HERE";
static void Main()
{
using (var webClient = new WebClient())
{
var json = webClient.DownloadString(Url);
var candidate_addresses = JsonConvert.DeserializeObject<CandidateAddress[]>(json);
foreach (var item in candidate_addresses)
Console.WriteLine(item.metadata.county_name);
Console.ReadLine();
}
}
}
public class CandidateAddress
{
public int input_index { get; set; }
public int candidate_index { get; set; }
public string delivery_line_1 { get; set; }
public string last_line { get; set; }
public string delivery_point_barcode { get; set; }
public Components components { get; set; }
public Metadata metadata { get; set; }
public Analysis analysis { get; set; }
}
public class Components
{
public string primary_number { get; set; }
public string street_name { get; set; }
public string street_suffix { get; set; }
public string city_name { get; set; }
public string state_abbreviation { get; set; }
public string zipcode { get; set; }
public string plus4_code { get; set; }
public string delivery_point { get; set; }
public string delivery_point_check_digit { get; set; }
}
public class Metadata
{
public string record_type { get; set; }
public string county_fips { get; set; }
public string county_name { get; set; }
public string carrier_route { get; set; }
public string congressional_district { get; set; }
public double latitude { get; set; }
public double longitude { get; set; }
public string precision { get; set; }
}
public class Analysis
{
public string dpv_match_code { get; set; }
public string dpv_footnotes { get; set; }
public string dpv_cmra { get; set; }
public string dpv_vacant { get; set; }
public bool ews_match { get; set; }
public string footnotes { get; set; }
}
}
So, in the end it will depend on whether you want to work with a statically typed response object or a dynamic one. Good luck!
EDIT: Included Latitude/Longitude fields in sample response (newly released).
I prefer to use dynamic object in these cases (No need for creating ugly classes)
such as:
dynamic jsonObj = JsonUtils.JsonObject.GetDynamicJsonObject(json);
foreach(var item in jsonObj)
{
Console.WriteLine(item.delivery_line_1 + ", " +
item.last_line + ", " +
item.metadata.county_name + ", " +
item.components.street_name + ", " +
item.components.city_name );
}
Here is the source for Dynamic Json Object (just copy & paste to your project) and some samples
PS: This is your json string in more readable format
[
{
"input_index": 0,
"candidate_index": 0,
"delivery_line_1": "1600 Amphitheatre Pkwy",
"last_line": "Mountain View CA 94043-1351",
"delivery_point_barcode": "940431351000",
"components": {
"primary_number": "1600",
"street_name": "Amphitheatre",
"street_suffix": "Pkwy",
"city_name": "Mountain View",
"state_abbreviation": "CA",
"zipcode": "94043",
"plus4_code": "1351",
"delivery_point": "00",
"delivery_point_check_digit": "0"
},
"metadata": {
"record_type": "S",
"county_fips": "06085",
"county_name": "Santa Clara",
"carrier_route": "C058",
"congressional_district": "14"
},
"analysis": {
"dpv_match_code": "Y",
"dpv_footnotes": "AABB",
"dpv_cmra": "N",
"dpv_vacant": "N",
"ews_match": false,
"footnotes": "N#"
}
}
]

Categories

Resources