I've been trying create c# classes to map to a JSON format required by a service. But failing to find the right answer.
Here is the JSON:
{
"request": {
"path": "1",
"coverages": {
"path": "2",
"broadcastCoverage": {
"path": "3",
"name": "First Coverage",
"channel": "Channel 9",
"callSign": "DSOTM"
},
"internetCoverage": {
"path": "4",
"name": "Second Coverage",
"url": "www.stackoverflow.com"
},
"thirdCoverage": {
"path": "5",
"name": "Third Coverage",
"differentProperty": "Units"
}
}
}
}
If I put this into a JSON to C# converter I get the following:
public class BroadcastCoverage
{
public string path { get; set; }
public string name { get; set; }
public string channel { get; set; }
public string callSign { get; set; }
}
public class InternetCoverage
{
public string path { get; set; }
public string name { get; set; }
public string url { get; set; }
}
public class ThirdCoverage
{
public string path { get; set; }
public string name { get; set; }
public string differentProperty { get; set; }
}
public class Coverages
{
public string path { get; set; }
public BroadcastCoverage broadcastCoverage { get; set; }
public InternetCoverage internetCoverage { get; set; }
public ThirdCoverage thirdCoverage { get; set; }
}
public class Request
{
public string path { get; set; }
public Coverages coverages { get; set; }
}
public class RootObject
{
public Request request { get; set; }
}
But I need different types of Coverages (Broadcast, Internet, others) to be variable so I tried taking those out of the Coverages class and added a property:
public Dictionary<string, CoverageBase> CoverageList { get; set; }
Which will allow me to choose which coverages to include, the problem then becomes the CoverageList property name is in the JSON when it is serialized. I essentially would like a key/value (string, CoverageBase) without the property name.
Is there a way to add key value pairs without having the property name in the JSON? I've seen examples where this is done at the root object level but I haven't been able to find any example where it nested within the JSON.
If this can't be done with a simple object model what would be a recommended method to get the JSON built?
UPDATE: I like the answer that utilizes JsonSubTypes as it doesn't require much code, however I can't use a 3rd party library outside of json.net. Is there a way to accomplish this using a JsonConverter?
I think its possible as checked here, however it seems your app needs to reconstruct the json in a format where it includes the C# typings. More documentation here.
EDIT:
Thanks to dbc's reference I was able to dive in to the JsonSubtypes and its pretty easy to implement.
Here's my code base structure.
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(BroadcastCoverage), "channel")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(InternetCoverage), "url")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(ThirdCoverage), "differentProperty")]
public class CoverageBase
{
public string path { get; set; }
public string name { get; set; }
}
public class BroadcastCoverage : CoverageBase
{
public string channel { get; set; }
public string callSign { get; set; }
}
public class InternetCoverage : CoverageBase
{
public string url { get; set; }
}
public class ThirdCoverage : CoverageBase
{
public string differentProperty { get; set; }
}
public class Request
{
public string path { get; set; }
public List<CoverageBase> coverages { get; set; }
}
However the Json you're receiving was not quite ideally structured, so I did some reformatting just to let it to be properly parsed.
string json = "{\"request\":{\"path\":\"1\",\"coverages\":{\"path\":\"2\",\"broadcastCoverage\":{\"path\":\"3\",\"name\":\"First Coverage\",\"channel\":\"Channel 9\",\"callSign\":\"DSOTM\"},\"internetCoverage\":{\"path\":\"4\",\"name\":\"Second Coverage\",\"url\":\"www.stackoverflow.com\"},\"thirdCoverage\":{\"path\":\"5\",\"name\":\"Third Coverage\",\"differentProperty\":\"Units\"}}}}";
var jsonReq = JObject.Parse(json);
var pathVal = jsonReq["request"]["path"].Value<string>();
var coverageObjects = jsonReq["request"]["coverages"].Value<JObject>();
var filteredObjects = coverageObjects.Children().Where(x => x.Value<JProperty>().Name.EndsWith("Coverage"));
var dictionary = filteredObjects.Select(x => new KeyValuePair<string, string>(x.Value<JProperty>().Name, x.Value<JProperty>().Value.ToString()));
// reformatted Json
var newJson = "{ \"path\":\"" + pathVal + "\", \"coverages\" : [" + String.Join(",", dictionary.Select(x => x.Value).ToList()) + "]}";
Request reqC = JsonConvert.DeserializeObject<Request>(newJson);
Related
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(),
});
I'm trying to deserialize JSON without declaring every property in C#. Here is a cut-down extract of the JSON:
{
"resourceType": "export",
"type": "search",
"total": 50,
"timestamp": "2020-08-02T18:26:06.747+00:00",
"entry": [
{
"url": "test.com/123",
"resource": {
"resourceType": "Slot",
"id": [
"123"
],
"schedule": {
"reference": {
"value": "testvalue"
}
},
"status": "free",
"start": "2020-08-03T08:30+01:00",
"end": "2020-08-03T09:00+01:00"
}
}
]
}
I want to get the values out of entry → resource, id and start.
Any suggestions on the best way to do this?
I've made very good experiences with json2sharp. You can enter your JSON data there and it will generate the classes you need to deserialize the JSON data for you.
public class Reference
{
public string value { get; set; }
}
public class Schedule
{
public Reference reference { get; set; }
}
public class Resource
{
public string resourceType { get; set; }
public List<string> id { get; set; }
public Schedule schedule { get; set; }
public string status { get; set; }
public string start { get; set; }
public string end { get; set; }
}
public class Entry
{
public string url { get; set; }
public Resource resource { get; set; }
}
public class Root
{
public string resourceType { get; set; }
public string type { get; set; }
public int total { get; set; }
public DateTime timestamp { get; set; }
public List<Entry> entry { get; set; }
}
The next step is to choose a framework which will help you to deserialize. Something like Newtonsoft JSON.
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
If you want to get the data without declaring classes, you can use Json.Net's LINQ-to-JSON API (JToken, JObject, etc.). You can use the SelectToken method with a JsonPath expression to get what you are looking for in a couple of lines. Note that .. is the recursive descent operator.
JObject obj = JObject.Parse(json);
List<string> ids = obj.SelectToken("..resource.id").ToObject<List<string>>();
DateTimeOffset start = obj.SelectToken("..resource.start").ToObject<DateTimeOffset>();
Working demo here: https://dotnetfiddle.net/jhBzl4
If it turns out there are actually multiple entries and you want to get the id and start values for all of them, you can use a query like this:
JObject obj = JObject.Parse(json);
var items = obj["entry"]
.Children<JObject>()
.Select(o => new
{
ids = o.SelectToken("resource.id").ToObject<List<string>>(),
start = o.SelectToken("resource.start").ToObject<DateTimeOffset>()
})
.ToList();
Demo: https://dotnetfiddle.net/Qe8NB7
I am not sure why you don't deserialize the lot (even if it's minimally populated) since you have to do the inner classes anyway.
Here is how you could bypass some of the classes (1) by digging into the JObjects
Given
public class Reference
{
public string value { get; set; }
}
public class Schedule
{
public Reference reference { get; set; }
}
public class Resource
{
public string resourceType { get; set; }
public List<string> id { get; set; }
public Schedule schedule { get; set; }
public string status { get; set; }
public string start { get; set; }
public string end { get; set; }
}
public class Entry
{
public string url { get; set; }
public Resource resource { get; set; }
}
You could call
var results = JObject.Parse(input)["entry"]
.Select(x => x.ToObject<Entry>());
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!
I need to deserialize a json which has got property names with a 'space' in between them ('Associated Team' and 'Point of Contact'). I have tried deserializing the json string by creating a strongly typed object but it is unable to map these 2 properties.
JSON string: (jsonString)
{
"id": "/subscriptions/911yyy-1234-4695-a90f-943xxxxxxx/resourceGroups/sample",
"name": "sample",
"type": null,
"properties": {
"provisioningState": "Succeeded"
},
"location": "westus",
"tags": {
"Associated Team": "Sample Team",
"Description": "Resource Group for Azure",
"Point of Contact": "abc#xyz.com"
}
}
.Net code snippet:
var deserializedResourceGroupDetails = JsonConvert.DeserializeObject<AzureResourceData>(jsonString);
AzurResourceData.cs class:
public class Tags
{
[JsonProperty("associatedTeam")]
public string associatedTeam { get; set; }
public string description { get; set; }
[JsonProperty("pointOfContact")]
public string pointOfContact { get; set; }
}
public class Properties
{
public string provisioningState { get; set; }
}
public class AzureResourceData
{
public string id { get; set; }
public string name { get; set; }
public string location { get; set; }
public Tags tags { get; set; }
public Properties properties { get; set; }
}
I have also tried deserializing the json dynamically(below) but then again I am unable to get the values of the two properties because they have got space in between their names.
dynamic deserializedResourceGroupDetails = JsonConvert.DeserializeObject(jsonString)));
Your [JsonProperty] should exactly match the key of your JSON object. So your Tags class should look like this:
public class Tags
{
[JsonProperty("Associated Team")] //this one changed
public string associatedTeam { get; set; }
public string description { get; set; }
[JsonProperty("Point of Contact")] //this one too
public string pointOfContact { get; set; }
}
This way, JSON knows where to map those keys in your file that aren't literally in your code.
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#"
}
}
]