How to deserialize where keys are numbers [closed] - c#

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Any thoughts on how to create the C# class for how to deserialise this correctly?
{
"key": "MobileNetworkOperator",
"name": "MNO Option",
"type": "n",
"options": [
{
"0": "Spark LTE M1"
},
{
"1": "Default"
},
{
"2": "AT&T"
},
{
"3": "Verizon"
},
{
"4": "Telstra"
},
{
"5": "T-Mobile USA"
},
{
"6": "China Telecom"
},
{
"7": "Sprint"
},
{
"8": "Vodafone"
},
{
"9": "Telus"
},
{
"10": "Deutsche Telecom"
},
{
"11": "Standard Europe"
}
],
"rebootRequired": true,
"default": 0
},
json2csharp.com gives the classes below but it does not seem to work. After deserialise, the right number of options items are present but all fields in each Option are set to null. I presume because the members are called '_0' instead of the actual '0' in the JSON???
public class Option
{
public string _0 { get; set; }
public string _1 { get; set; }
public string _2 { get; set; }
public string _3 { get; set; }
public string _4 { get; set; }
public string _5 { get; set; }
public string _6 { get; set; }
public string _7 { get; set; }
public string _8 { get; set; }
public string _9 { get; set; }
public string _10 { get; set; }
public string _11 { get; set; }
}
public class Root
{
public string key { get; set; }
public string name { get; set; }
public string type { get; set; }
public List<Option> options { get; set; }
public bool rebootRequired { get; set; }
public int #default { get; set; }
}
Note that this is part of a larger object with many of these items in which there can be no or many options in each item, but I tried to simplify it down.
I am using the System.Text.Json parser. Thanks in advance for your advice. This is my first go at JSON in C#!

OP stated that they are using System.Text.Json as their parser. So, with that being known, we will need to create a custom JsonConverter.
Start by creating an Option class:
public class Option
{
public int Key { get; set; }
public string Value { get; set; }
}
Next, create our JsonConverter implementation based on the Option object from above:
public class MyOptionConverter : JsonConverter<Option>
{
public override Option Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
reader.Read(); // read in the first token
var key = int.Parse(reader.GetString()); // get the value as an integer
reader.Read(); // read in the next token
var value = reader.GetString(); // get the value as string
reader.Read(); // read in End-of-object
return new Option { Key = key, Value = value }; // return our new object
}
public override void Write(
Utf8JsonWriter writer, Option value, JsonSerializerOptions options)
{
throw new NotImplementedException(); // we aren't going to handle writing for now.
}
}
Let's create a model that represents your data:
public class Root
{
[JsonPropertyName("key")]
public string Key { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("options")]
public IReadOnlyList<Option> Options { get; set; }
[JsonPropertyName("rebootRequired")]
public bool RebootRequired { get; set; }
[JsonPropertyName("default")]
public int Default { get; set; }
}
Then to deserialize, you would simply do this:
var options = new JsonSerializerOptions();
options.Converters.Add(new MyOptionConverter());
var myObject = JsonSerializer.Deserialize<Root>(theJson, options);
Edited To Add:
#Llama brought it to my attention (in the comments) that OP is probably using Newtonsoft and not System.Text.Json.
Someone already posted the Newtonsoft solution, but the author deleted it even though it was probably the correct answer.
I am going to leave this answer up just in case it may help someone else.

Deserializing a JSon I will always go for a class representing exactly the json structure/
Class definition using System.Text.Json:
public partial class JsonDto
{
public string key { get; set; }
public string name { get; set; }
public string type { get; set; }
public List<Dictionary<string, string>> options { get; set; }
public bool rebootRequired { get; set; }
public long #default { get; set; }
}
Class definition using Newtonsoft.Json, is the same. Json property in order to have capitilized properties.
public partial class Root
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("options")]
public List<Dictionary<string, string>> Options { get; set; }
[JsonProperty("rebootRequired")]
public bool RebootRequired { get; set; }
[JsonProperty("default")]
public long Default { get; set; }
}
The deserialization is almost the same
var result = JsonConvert.DeserializeObject<JsonDto>(input); // Newtonsoft.Json
var result = JsonSerializer.Deserialize<JsonDto>(input);
Then a simple transpormation where we ger rid of the extra list in List<Dictionary<string, string>>.
var result =
new UsableObject {
Key=jsonDto.key,
Name=jsonDto.name,
Type=jsonDto.type,
RebootRequired=jsonDto.rebootRequired,
Default=jsonDto.#default,
Options=jsonDto.options
.SelectMany(trd=> trd)
.ToDictionary(kvp=> kvp.Key, kvp=> kvp.Value),
};
Live demo : https://dotnetfiddle.net/c9rNL0

Related

How to generate a JSON class with dynamic name

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(),
});

Deserialize JSON using specific properties

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

Deserialize JSON array which has mixed values System.Text.JSON

I'm trying to create a page rendered in .net core 3.1 which renders pages based on JSON.
How can I deserialzie the JSON at the end of this post?
I've tried to deserialize this however it doesn't work because I loose the data for each Component,
since the Page class has a List<Component> - but I need this to be a list of varying different components.
Page Model :
public class Page
{
public int id { get; set; }
public string pagename { get; set; }
public string metatitle { get; set; }
public string metadescription { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
public List<Component> components { get; set; }
}
public class Pages
{
public List<Page> pages { get; set; }
}
Component Model:
public class Component
{
public string component { get; set; }
public int id { get; set; }
}
A Component :
public class Title : Component
{
public string component { get; set; }
public int id { get; set; {
public string titletext { get; set; }
}
This is the JSON:
{
"id":1,
"pagename":"home",
"metatitle":"no title",
"metadescription":"no meta",
"created_at":"2020-05-31T16:35:52.084Z",
"updated_at":"2020-05-31T16:35:52.084Z",
"components":[
{
"component":"components.titletext",
"id":1,
"titletext":"hello"
},
{
"component":"components.section",
"id":2,
"titletext":"hello",
"descriptiontext":"its a beatiful day in lost santos",
"buttonlink":"/go"
},
{
"component":"components.cta",
"id":3,
"sectiontitle":"hello",
"buttonlink":"/go",
"buttontext":"click me"
}
]
}
If you don't want to add all properties to the Component class like that:
public class Component
{
public string component { get; set; }
public int id { get; set; }
public string titletext { get; set; }
public string sectiontitle { get; set; }
public string buttonlink { get; set; }
public string descriptiontext { get; set; }
}
You will need to write custom JsonConverter for example (not very performant implementation but works with your json and you will not need to parse every field by hand):
public class ComponentConverter : JsonConverter<Component>
{
public override Component Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (var doc = JsonDocument.ParseValue(ref reader))
{
var type = doc.RootElement.GetProperty(#"component").GetString();
switch(type)
{
case "components.titletext":
return JsonSerializer.Deserialize<Title>(doc.RootElement.GetRawText());
// other types handling
default: return JsonSerializer.Deserialize<Component>(doc.RootElement.GetRawText());
}
}
}
public override void Write(Utf8JsonWriter writer, Component value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
public class Component
{
public string component { get; set; }
public int id { get; set; }
}
public class Title : Component
{
public string titletext { get; set; }
}
And usage example:
var json = #"[
{
""component"":""components.titletext"",
""id"":1,
""titletext"":""hello""
},
{
""component"":""components.section"",
""id"":2,
""titletext"":""hello"",
""descriptiontext"":""its a beatiful day in lost santos"",
""buttonlink"":""/go""
},
{
""component"":""components.cta"",
""id"":3,
""sectiontitle"":""hello"",
""buttonlink"":""/go"",
""buttontext"":""click me""
}
]";
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.Converters.Add(new ComponentConverter());
JsonSerializer.Deserialize<List<Component>>(json, deserializeOptions).Dump();
Also do not use this converter as parameter for JsonConverterAttribute cause it will end in stackoverflow.
If you want/need completely separate unrelated classes you could use a technique that doesn't use a converter:
var ays = new List<A>();
var bees = new List<B>();
using var doc = JsonDocument.Parse(json);
foreach (var block in doc.RootElement.EnumerateArray())
{
switch (block.GetProperty("component").GetString())
{
case "typeA": ays.Add(Deserialise<A>(block)); break;
case "typeB": bees.Add(Deserialise<B>(block)); break;
// ... case
//default: ...
}
}
var composite = new
{
As = ays,
Bs = bees
};
// This is OK, but if you need to speed it up, please have a look at
// https://stackoverflow.com/questions/58138793/system-text-json-jsonelement-toobject-workaround
static T Deserialise<T>(JsonElement e) => JsonSerializer.Deserialize<T>(e.GetRawText(), options: new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

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!

JSON field values become null after deserialization

I have a JSON string like this
{
"data": {
"id": "f4ba528a54117950",
"type": "password-requests",
"links": {
"self": "https://api.abc.com/api/v2/password-requests/f4ba528a54117950"
},
"attributes": {
"login": "abc",
"type": "agent",
"send-media": false,
"registration-token": "ced84635eba"
}
}
}
My classes are like this
public class SightCallResult
{
public SightCallData data { get; set; }
}
public class SightCallData
{
public string id { get; set; }
public string type { get; set; }
public Dictionary<string, string> links { get; set; }
public AgentAttributes attributes { get; set; }
}
public class AgentAttributes
{
public string Login { get; set; }
public string Type { get; set; }
public bool SendMedia { get; set; }
public string RegistrationToken { get; set; }
}
This is how I deserialize my string
sightCallRslt = JsonConvert.DeserializeObject<SightCallResult>(resultMobileToken);
sightCallData = sightCallRslt.data;
agentAttributes = sightCallData.attributes;
Debug.WriteLine(agentAttributes.RegistrationToken);
But RegistrationToken is always null. But other field values are correctly assigned. Could anybody explain what would be the reason for this.
I think you are using Newtonsoft.Json which won't automatically map a hyphenated key name to a PascalCase key name.
You maybe didn't notice it for e.g. send-media because its a non nullable / defaults to false.
If you can't change the json, you can decorate the attributes with JsonProperty:
[JsonProperty(PropertyName="send-media")]
public bool SendMedia { get; set; }
[JsonProperty(PropertyName="registration-token")]
public string RegistrationToken { get; set; }
Either change the type of attributes to Dictionary<string, object>, or if you are sure there are a finite amount of possible attributes use the JsonPropertyAttribute to specify the exact names:
[JsonProperty("registration-token")]
public string RegistrationToken { get; set; }

Categories

Resources