How to generate a JSON class with dynamic name - c#

I don't know if there is an existing name for that case, but I'm trying to retrieve data from NASA API (https://api.nasa.gov/) and I have a simple challenge to catch a list of objects near earth. Here is the JSON response I have from the GET request I do to "https://api.nasa.gov/neo/rest/v1/feed?...."
{
"links": {
"next": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-04&end_date=2021-07-04&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"prev": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-02&end_date=2021-07-02&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"self": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-03&end_date=2021-07-03&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"element_count": 6,
"near_earth_objects": {
"2021-07-03": [
{
"links": {
"self": "http://www.neowsapp.com/rest/v1/neo/3701710?api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"id": "3701710",
"neo_reference_id": "3701710",
"name": "(2014 WF497)",
"nasa_jpl_url": "http://ssd.jpl.nasa.gov/sbdb.cgi?sstr=3701710",
"absolute_magnitude_h": 20.23,
"estimated_diameter": {
"kilometers": {
}
And that's the way it is built in Visual Studio (using the Special Paste option for JSON)
public class NearEarthObject
{
public Links links { get; set; }
public int element_count { get; set; }
public Near_Earth_Objects near_earth_objects { get; set; }
}
public class Links
{
public string next { get; set; }
public string prev { get; set; }
public string self { get; set; }
}
public class Near_Earth_Objects
{
public _20210703[] _20210703 { get; set; }
}
public class _20210703
{
public Links1 links { get; set; }
public string id { get; set; }
public string neo_reference_id { get; set; }
public string name { get; set; }
public string nasa_jpl_url { get; set; }
public float absolute_magnitude_h { get; set; }
public Estimated_Diameter estimated_diameter { get; set; }
public bool is_potentially_hazardous_asteroid { get; set; }
public Close_Approach_Data[] close_approach_data { get; set; }
public bool is_sentry_object { get; set; }
}
The question is, inside of the element "near_earth_objects", there is an element called "2021-07-03" (the date of the data I requested), the problem is that I am trying to include it into a DataGridView made in .NET C# (Windows Forms, but that doesn't matters here, I think) and the user wants to get the information by date. So, "2021-07-03" is a valid member just for one day, and the user should be able to get data from multiple days.
So, is there a way in C# to get all child objects inside of near_earth_objects without knowing their names since there will be the option to search for asteroids from date X to Y in my application?

Using System.Text.Json
The API response will map to the following classes
public class Neo
{
public Links Links { get; set; }
public int ElementCount { get; set; }
public Dictionary<string, List<NearEarthObject>> NearEarthObjects { get; set; }
}
public class Links
{
public string Next { get; set; }
public string Prev { get; set; }
public string Self { get; set; }
}
public class NearEarthObject
{
public Links Links { get; set; }
public string Id { get; set; }
public string Name { get; set; }
// Other properties
}
The NearEarthObjects is simply a Dictionary, where the key is the formatted date and value is a List containing NearEarthObject
The PropertyNamingPolicy will allow us to support the API's underscore property naming convention.
public class UnderscoreNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.Underscore();
}
}
Example usage
// using using System.Text.Json;
var response = await new HttpClient().GetStringAsync(url);
var neo = JsonSerializer.Deserialize<Neo>(response, new JsonSerializerOptions
{
PropertyNamingPolicy = new UnderscoreNamingPolicy()
});
foreach(var neos in neo.NearEarthObjects)
{
Console.WriteLine(neos.Key);
}

use System.Text.Json, JsonNamingPolicy
demo code
public class DynamicNamePolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
var today = DateTime.Today.ToString("yyyy-MM-dd");
if (name.Equals("DateData")) //model property name
return today; //convert to json string property name
return name;
}
}
//data deserialize
string data = ""; //json string
var obj = JsonSerializer.Deserialize<NearEarthObject>(data, new JsonSerializerOptions
{
PropertyNamingPolicy = new DynamicNamePolicy(),
});

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

Not able validate data condition based on the json element attribute value from a json using c#

I have a json file, where i have to validate a json attribute element value based on another json element attribute value. But if there json elements with the same name. It always takes the last value always instead of parsing the json data fully. Please guide me.
Below the sample json file
{
"PLMXML":{
"language":"en-us",
"author":"Developer",
"date":"2020-05-22",
"traverseRootRefs":"#id6",
"Operation":{
"id":"id21",
"subType":"BS4_BaOP",
"catalogueId":"70700000209604"
},
"Operation":{
"id":"id28",
"subType":"BS4_BaOP",
"catalogueId":"70700000209603"
},
"OperationRevision":{
"id":"id6",
"subType":"BS4_BaOPRevision",
"masterRef":"#id21",
"revision":"A1"
}
}
}
And below the code which im trying to use
public void Readjsonfile(string jsondata)
{
var message = JsonConvert.DeserializeObject<plmxmldatamodel>(jsondata);
if (String.Equals(message.PLMXML.traverseRootRefs.Substring(1), message.PLMXML.OperationRevision.id))
{
Console.WriteLine("Condtion1");
if (String.Equals(message.PLMXML.OperationRevision.masterRef.Substring(1), message.PLMXML.Operation.id))
{
Console.WriteLine("Condition_2");
//Do something based on the condtion
}
}
}
public class Operation
{
public string id { get; set; }
public string subType { get; set; }
public string catalogueId { get; set; }
}
public class OperationRevision
{
public string id { get; set; }
public string subType { get; set; }
public string masterRef { get; set; }
}
public class PLMXML
{
public string language { get; set; }
public string author { get; set; }
public string date { get; set; }
public string traverseRootRefs { get; set; }
public Operation Operation { get; set; }
public OperationRevision OperationRevision { get; set; }
}
public class plmxmldatamodel
{
public PLMXML PLMXML { get; set; }
}
When i try to dedug this in the second if condtion, the value for message.PLMXML.Operation.id is always id28 , because of which second if condition fails. While the first if condition is passed as there is only one message.PLMXML.OperationRevision.id. i wanted behaviour where it would check complete json data and check if message.PLMXML.Operation.id with value id21 is present or not , So my data gets passed. Please kindly guide me here.I am very new to C# here.
From my observation you have couple of issues.
What happen you have double keys, and your parser taking the last value not the first one.
First of all your json should be corrected. I assume you have access to change your json and operation should be an array like follow:
{
"PLMXML":{
"language":"en-us",
"author":"Developer",
"date":"2020-05-22",
"traverseRootRefs":"#id6",
"Operations":[
{
"id":"id21",
"subType":"BS4_BaOP",
"catalogueId":"70700000209604"
},
{
"id":"id28",
"subType":"BS4_BaOP",
"catalogueId":"70700000209603"
}
],
"OperationRevision":{
"id":"id6",
"subType":"BS4_BaOPRevision",
"masterRef":"#id21",
"revision":"A1"
}
}
}
When array in place than use an online tool like to validate your json and use this tool to create a model.
Your model will be like this:
public partial class PlmxmlDataModel
{
[JsonProperty("PLMXML")]
public Plmxml Plmxml { get; set; }
}
public partial class Plmxml
{
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("author")]
public string Author { get; set; }
[JsonProperty("date")]
public DateTimeOffset Date { get; set; }
[JsonProperty("traverseRootRefs")]
public string TraverseRootRefs { get; set; }
[JsonProperty("Operations")]
public Operation[] Operations { get; set; }
[JsonProperty("OperationRevision")]
public OperationRevision OperationRevision { get; set; }
}
public partial class OperationRevision
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("subType")]
public string SubType { get; set; }
[JsonProperty("masterRef")]
public string MasterRef { get; set; }
[JsonProperty("revision")]
public string Revision { get; set; }
}
public partial class Operation
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("subType")]
public string SubType { get; set; }
[JsonProperty("catalogueId")]
public string CatalogueId { get; set; }
}
And your method like this:
public void Readjsonfile(string jsondata)
{
var message = JsonConvert.DeserializeObject<PlmxmlDataModel>(jsondata);
if (String.Equals(message.Plmxml.TraverseRootRefs.Substring(1), message.Plmxml.OperationRevision.Id))
{
Console.WriteLine("Condtion1");
if (String.Equals(message.Plmxml.OperationRevision.MasterRef.Substring(1), message.Plmxml.Operations[0].Id))
{
Console.WriteLine("Condition_2");
//Do something based on the condtion
}
}
}
Now in your method I am looking for array index 0 with contain id 28, but if you are look for id 28 in any of the array then you can do some thing like:
if (message.Plmxml.Operations.Any(e => e.Id == message.Plmxml.OperationRevision.MasterRef.Substring(1)))

How to deserialize multidimensional JSON

I know people asked and already got some answers very similar question before like this, but still, I couldn't figure it out about mine. I have a JSON file contains a multidimensional object, like below:
{
"Common": {
"Required": "Required Entry ",
"Photos": "Photos",
"Videos": "Videos",
"Register": "Register"
},
"Forms": {
"Form": "Forms",
"Name": "Name",
"Phone": "Phone",
"Email": "Email",
"Message": "Message"
},
"Sections": {
"Home": {
"EventDateTime": "",
"MainTitle": "",
"SubTitle": ""
},
"About": {},
"Venue": {},
"Schedule": {},
"Speakers": {},
"Sponsors": {},
"Price": {},
"Contact": {}
}
}
I would like to deserialize it into my view model (LanguagesViewModel) like this:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class LanguagesViewModel
{
public Common Common { get; set; }
public Buttons Buttons { get; set; }
public Forms Forms { get; set; }
public Navbar Navbar { get; set; }
public Sections Sections { get; set; }
}
public class Common
{
public string Required { get; set; }
public string Photos { get; set; }
public string Videos { get; set; }
public string Register { get; set; }
}
public class Forms
{
public string Form { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Message { get; set; }
}
public class Sections
{
public Home Home { get; set; }
public About About { get; set; }
public Venue Venue { get; set; }
public Schedule Schedule { get; set; }
public Speakers Speakers { get; set; }
public Sponsors Sponsors { get; set; }
public Price Price { get; set; }
public Contact Contact { get; set; }
}
public class Home
{
public string EventDateTime { get; set; }
public string MainTitle { get; set; }
public string SubTitle { get; set; }
}
public class About
{
}
public class Venue
{
}
public class Schedule
{
}
public class Speakers
{
}
public class Sponsors
{
}
public class Price
{
}
public class Contact
{
}
}
Some of the snippet to do this:
using (StreamReader sr = new StreamReader(language_file_path))
{
string contents = sr.ReadToEnd();
items = JsonConvert.DeserializeObject<LanguagesViewModel>(contents);
}
Somehow, I only can get the first level of the objects, which is:
LanguagesViewModel{
Common:null,
Forms:null,
Sections:null
}
Not the second level, not the third level. Did I do something wrong or have I missed something? Very appreciated for any kind of help.
Thank you.
You can Use this static class
public static class JsonHelper
{
public static T ToObject<T>(this string content)
{
var obj = JObject.Parse(content).GetValue(typeof(T).Name);
if (obj == null)
throw new NullReferenceException();
else
return obj.ToObject<T>();
//This ToObject here is default method written in object
}
}
Usage
var mymodel= json.ToObject<Forms>();
Or create a JSON object and read it with magic strings.
//Creating your JSON object
JObject content = JObject.Parse(sr.ReadToEnd()//or your json);
//content["your object name"] let you access to you object
var common =(Common)content["Common"];
in multidimensional objects, you can access them like this.
//content["level1"]["level2"]["level3"] & ...
var sections= (Home)content["Sections"]["Home"];
Also this way may work but i prefer the way with magic strings.
dynamic jsonObject = new JObject.Parse(sr.ReadToEnd());
var common = jsonObject.Common;
You can find more in this link
I hope this Helps!

nested json deserialization - need another set of eyes

I have been trying to get this json to deserialize for two days now using RestSharp. I have gone through the RestSharp github site, looked at countless examples, and spent much time here on Stack Overflow to try and find the answer to no avail. My code had previously worked perfectly but the vendor changed their API version and I was forced to do an update to keep using the application for my legal practice. My json is as follows(client info has been removed and replaced with generic info):
{
"data":[
{
"id":1035117666,
"client":
{
"id":905422394,
"name":"client1"
},
"display_number":"11-00012",
"description":"General",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2011-12-14",
"close_date":null,
"billing_method":"hourly"
},
{
"id":1035117768,
"client":
{
"id":905422506,
"name":"client2"
},
"display_number":"12-00037",
"description":"HOA",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2012-08-07",
"close_date":null,
"billing_method":"hourly"
}
],
"meta":
{
"paging":
{
"next":"https://app.goclio.com/api/v4/matters.json?fields=id%2C+client%7Bid%2C+name%7D%2C+display_number%2C+description%2C+practice_area%7Bid%2C+name%7D%2C+status%2C+open_date%2C+close_date%2C+billing_method&limit=2&page_token=BAh7BjoLb2Zmc2V0aQc%3D--b1ea3eba20c8acefbcdfc7868debd1e0ee630c64&status=Open"
},
"records":91
}
}
I built the following schema within my c# code:
public class MatterList
{
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
public class Meta
{
public Paging paging { get; set; }
public int records { get; set; }
}
public class Paging
{
public string previous { get; set; }
public string next { get; set; }
}
[DeserializeAs(Name = "data")]
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
public class PracticeArea
{
public int id { get; set; }
public string name { get; set; }
}
public class Client
{
public int id { get; set; }
public string name { get; set; }
}
When I run the RestSharp deserialize method I am sending the result to an object of type MatterList using the following line of code
MatterList matterList = jsonHandler.Deserialize<MatterList>(response);
I have so far attempted to deserialize without the Meta or Paging POCO classes with the accompanying change to the MatterList class (taking out the Meta property).
I have tried with and without the [DeserializeAs(Name="data")] directive.
I have tried to set the RootElement of the json response prior to deserialization.
I have tried to shorthand the deserialization by combining it with the Execute request code
IRestResponse<MatterList> matterList = client.Execute<MatterList>(request);
I have created a container class called MatterContainer which I placed between MatterList and Matter classes in the schema:
public class MatterList
{
public List<MatterContainer> matters { get; set; }
}
public class MatterContainer
{
public Matter matter { get; set; }
}
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
I know I am getting the json response back from the server correctly so my request is proper and MatterList is not null after deserialization. The problem is that I cannot get the deserialization to actually populate the List matters within the MatterList class.
I have been looking at this off and on for two days and cannot get past this hurdle. If anyone sees what I did wrong I would greatly appreciate the insight, I am at a point where I cannot progress further with my application.
Thanks!
I think your [DeserializeAs(Name = "data")] attribute is in the wrong place. Try putting it in the root class instead:
public class MatterList
{
[DeserializeAs(Name = "data")]
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
alternatively, try renameing that property to data

How to set up class for json?

I am working with opentdb.com api and have no idea how to access data in this json:
{
"response_code":0,
"results": [
{
"category":"Animals",
"type":"multiple",
"difficulty":"easy",
"question":"What is the scientific name for modern day humans?",
"correct_answer":"Homo Sapiens",
"incorrect_answers":[
"Homo Ergaster",
"Homo Erectus",
"Homo Neanderthalensis"
]
},
{
"category":"Entertainment: Cartoon & Animations",
"type":"multiple",
"difficulty":"easy",
"question":"Who voices for Ruby in the animated series RWBY?",
"correct_answer":"Lindsay Jones",
"incorrect_answers":[
"Tara Strong",
"Jessica Nigri",
"Hayden Panettiere"
]
}
]
}
I am using Newtonsoft.Json and i tried this with only 1 Question but im geting an error that says that key value is wrong..
class Trivia
{
public Trivia(string json)
{
JObject jObject = JObject.Parse(json);
JToken jresults = jObject["results"];
category = (string)jresults["category"];
type = (string)jresults["type"];
difficulty = (string)jresults["difficulty"];
question = (string)jresults["question"];
correct_answer = (string)jresults["correct_answer"];
incorrect_answers = jresults["incorrect_answers"].ToArray();
}
public string category { get; set; }
public string type { get; set; }
public string difficulty { get; set; }
public string question { get; set; }
public string correct_answer { get; set; }
public Array incorrect_answers { get; set; }
}
Copy from json text and in new class in visual studio Edit-->Paste Special-->Paste JSON As Classes
public class Rootobject
{
public int response_code { get; set; }
public Result[] results { get; set; }
}
public class Result
{
public string category { get; set; }
public string type { get; set; }
public string difficulty { get; set; }
public string question { get; set; }
public string correct_answer { get; set; }
public string[] incorrect_answers { get; set; }
}
using namespace
using Newtonsoft.Json;
response is value get from your service
var outdeserialized = JsonConvert.DeserializeObject<Rootobject>(response);

Categories

Resources