How do I deserialize a complex JSON object in C# .NET?
I am using like this in my code and it's working fine
below is a piece of code which you need to write
using System.Web.Script.Serialization;
JavaScriptSerializer oJS = new JavaScriptSerializer();
RootObject oRootObject = new RootObject();
oRootObject = oJS.Deserialize<RootObject>(Your JSon String);
How to Deserialize complex JSON and create it as C# Object
You've got JSON containing JSON, basically - so you should expect to have to deserialize once. (If you can change the structure of the JSON to avoid this double-serialization, that would be better, admittedly, but I'll assume that's fixed.)
For example, you could have:
public class Root
{
public List<Value> Values { get; set; }
}
public class Value
{
// Add other properties if you need them
public string RawValue { get; set; }
}
Then:
string json = ...;
Root root = JsonConvert.DeserializeObject<Root>(json);
// This just takes the first value - we don't know whether you actually
// ever have more than one...
string rawValue = root.Values[0].RawValue;
JArray array = JArray.Parse(rawValue);
That assumes you're happy using JArray
/JObject
for the "embedded" objects. If you want to model those as well, you'd have:
public class Station
{
[JsonProperty("ID")]
public int Id { get; set; }
public int StationNo { get; set; }
// etc
}
... then for deserializing:
string json = ...;
Root root = JsonConvert.DeserializeObject<Root>(json);
// This just takes the first value - we don't know whether you actually
// ever have more than one...
string rawValue = root.Values[0].RawValue;
List<Station> stations = JsonConvert.DeserializeObject<List<Station>>(rawValue);
There shouldn't be any "extra" escaping by the time you've deserialized twice.
Deserialize a complex json object c#
I would probably use Json.NET to parse the JSON to a JObject
, then extract the relevant values from that - using JToken.ToObject<>
where possible.
Here's some sample code that appears to work - but needs a lot of validation and general tidying up. It's more about demonstrating the idea than anything else:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Program
{
static void Main(string[] args)
{
string json = File.ReadAllText("test.json");
Result result = Result.FromJson(json);
Console.WriteLine(result.Status);
Console.WriteLine(result.Values.Count);
Console.WriteLine(result.Values[0].User.FirstName);
}
}
public sealed class Result
{
public string Status { get; }
public IReadOnlyList<ResultValue> Values { get; }
private Result(string status, IReadOnlyList<ResultValue> values) =>
(Status, Values) = (status, values);
public static Result FromJson(string json)
{
JObject parsed = JObject.Parse(json);
string status = (string) parsed["status"];
JArray array = (JArray) parsed["value"];
var values = array.Select(ResultValue.FromJToken).ToList().AsReadOnly();
return new Result(status, values);
}
}
public sealed class ResultValue
{
public DateTime Timestamp { get; }
public string Id { get; }
public string Email { get; }
public User User { get; }
private ResultValue(DateTime timestamp, string id, string email, User user) =>
(Timestamp, Id, Email, User) = (timestamp, id, email, user);
internal static ResultValue FromJToken(JToken token)
{
JArray array = (JArray) token;
DateTime timestamp = (DateTime) array[0];
string id = (string) array[1];
string email = (string) array[2];
User user = array[3].ToObject<User>();
return new ResultValue(timestamp, id, email, user);
}
}
// TODO: Make this immutable, or everything else immutable
public sealed class User
{
[JsonProperty("role")]
public string Role { get; set; }
[JsonProperty("company")]
public string Company { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("modification_date")]
public DateTime ModificationDate { get; set; }
}
How to deserialize complex JSON with Newtonsoft?
This is indeed a complex schema so it's not easy to generate DTOs automatically. For that reason, you shouldn't use custom names for the properties. It makes it even harder to find problems.
- The
near_earth_objects
section is actually a dictionary of daily observations. Instead of creating anear_earth_objects
class it's better to use aDictionary<string,Observation[]>
. links
contains links to the current, next and previous pages in the feed. This means you can actually create one class and reuse it both at the root level and in daily observations
You can use a DTO generator tool to get started but the result will need modifications. Tools won't be able to recognize that near_earth_objects
is a dictionary and can easily end up creating new types for every entry.
The DTOs
Using your JSON sample I created initial classes using Visual Studio's Paste as Json
and then modified them to work properly.
public class Rootobject
{
public PageLinks links { get; set; }
public int element_count { get; set; }
public Dictionary<string,Observation[]> near_earth_objects { get; set; }
}
public class PageLinks
{
public string? next { get; set; }
public string? prev { get; set; }
public string self { get; set; }
}
The Observation
class uses the same PageLinks
class for the links
property:
public class Observation
{
public PageLinks 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 rest of the classes require no modification:
public class Estimated_Diameter
{
public Kilometers kilometers { get; set; }
public Meters meters { get; set; }
public Miles miles { get; set; }
public Feet feet { get; set; }
}
public class Kilometers
{
public float estimated_diameter_min { get; set; }
public float estimated_diameter_max { get; set; }
}
public class Meters
{
public float estimated_diameter_min { get; set; }
public float estimated_diameter_max { get; set; }
}
public class Miles
{
public float estimated_diameter_min { get; set; }
public float estimated_diameter_max { get; set; }
}
public class Feet
{
public float estimated_diameter_min { get; set; }
public float estimated_diameter_max { get; set; }
}
public class Close_Approach_Data
{
public string close_approach_date { get; set; }
public string close_approach_date_full { get; set; }
public long epoch_date_close_approach { get; set; }
public Relative_Velocity relative_velocity { get; set; }
public Miss_Distance miss_distance { get; set; }
public string orbiting_body { get; set; }
}
public class Relative_Velocity
{
public string kilometers_per_second { get; set; }
public string kilometers_per_hour { get; set; }
public string miles_per_hour { get; set; }
}
public class Miss_Distance
{
public string astronomical { get; set; }
public string lunar { get; set; }
public string kilometers { get; set; }
public string miles { get; set; }
}
Testing the model
With this model, the following test passes:
[Fact]
public async Task GetFeed()
{
var client = new HttpClient();
var url = "http://www.neowsapp.com/rest/v1/feed?start_date=2021-12-09&end_date=2021-12-12&detailed=false&api_key=Na1sKwJGK1HVeOF4Yx8aLNp4u8ygT5GSSMF26HQ2";
var feed = await client.GetFromJsonAsync<Rootobject>(url);
Assert.Equal(76,feed.element_count);
var allObservations = feed.near_earth_objects
.SelectMany(p => p.Value)
.ToList();
Assert.Equal(76,allObservations.Count);
}
Deserialize complex JSON object using c#
You are looking for a dictionary.
Use this as your class definition:
public class Rootobject
{
public Dictionary<string, DataObject> data { get; set; }
public string type { get; set; }
public string version { get; set; }
}
public class DataObject
{
public int id { get; set; }
public string key { get; set; }
public string name { get; set; }
public string title { get; set; }
}
And this demonstrates that reading your object works:
var vals = @"{
""data"": {
""A"": {
""id"": 24,
""key"": ""key"",
""name"": ""name"",
""title"": ""title""
},
""B"": {
""id"": 37,
""key"": ""key"",
""name"": ""name"",
""title"": ""title""
},
""C"": {
""id"": 18,
""key"": ""key"",
""name"": ""name"",
""title"": ""title""
},
""D"": {
""id"": 110,
""key"": ""key"",
""name"": ""name"",
""title"": ""title""
}
},
""type"": ""type"",
""version"": ""1.0.0""
}";
var obj = JsonConvert.DeserializeObject<Rootobject>(vals);
Deserialize a complex JSON response without property names
Try this. It was tested in Visual Studio
var parsedArray=JArray.Parse(json);
// this is just an array part
List<WorldBankApiResponse> worldBankApiResponses =
JsonConvert.DeserializeObject<List<WorldBankApiResponse>>(parsedArray[1].ToString());
// this is just a header part
var wb =
JsonConvert.DeserializeObject<WorldBankApiResponseGlobal>(parsedArray[0].ToString());
//this is the whole object
var worldBankApiResponseGlobal= new WorldBankApiResponseGlobal{
Page=wb.Page,
Pages=wb.Pages,
PerPage=wb.PerPage,
Total=wb.Total,
Sourceid=wb.Sourceid,
Sourcename=wb.Sourcename,
Lastupdated=wb.Lastupdated,
WorldBankApiResponses= worldBankApiResponses
};
classes
public partial class WorldBankApiResponseGlobal
{
[JsonProperty("page")]
public long Page { get; set; }
[JsonProperty("pages")]
public long Pages { get; set; }
[JsonProperty("per_page")]
public long PerPage { get; set; }
[JsonProperty("total")]
public long Total { get; set; }
[JsonProperty("sourceid")]
public long Sourceid { get; set; }
[JsonProperty("sourcename")]
public string Sourcename { get; set; }
[JsonProperty("lastupdated")]
public DateTimeOffset Lastupdated { get; set; }
[JsonProperty("WorldBankApiResponses")]
public List<WorldBankApiResponse> WorldBankApiResponses { get; set; }
}
public partial class WorldBankApiResponse
{
[JsonProperty("indicator")]
public WorldBankApiIdValue Indicator { get; set; }
[JsonProperty("country")]
public WorldBankApiIdValue Country { get; set; }
[JsonProperty("countryiso3code")]
public string Countryiso3Code { get; set; }
[JsonProperty("date")]
public long Date { get; set; }
[JsonProperty("value")]
public double Value { get; set; }
[JsonProperty("unit")]
public string Unit { get; set; }
[JsonProperty("obs_status")]
public string ObsStatus { get; set; }
[JsonProperty("decimal")]
public long Decimal { get; set; }
}
public partial class WorldBankApiIdValue
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
}
How to deserialize nested JSON in C#
All code was tested using VS2019 and working properly.
The simpliest way will be
var root =Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = root + @"\SubtitleTranslator\LanguagesList.json";
var json = File.ReadAllText(path);
var jsonDeserialized = JsonConvert.DeserializeObject<Root>(json);
List<Language> languages = null;
if( jsonDeserialized.StatusCode== 200) languages=jsonDeserialized.Result.languages;
or if you don't need any data except languages, try this code
var languages= JObject.Parse(json)["Result"]["languages"].ToObject<Language[]>();
in this case you will need only Language class
OUTPUT (in both cases)
[{"language":"af","language_name":"Afrikaans","native_language_name":"Afrikaans","country_code":"ZA","words_separated":true,"direction":"left_to_right","supported_as_source":false,"supported_as_target":false,"identifiable":true},
{"language":"ar","language_name":"Arabic","native_language_name":"العربية","country_code":"AR","words_separated":true,"direction":"right_to_left","supported_as_source":true,"supported_as_target":true,"identifiable":true},
{"language":"az","language_name":"Azerbaijani","native_language_name":"آذربایجان دیلی","country_code":"AZ","words_separated":true,"direction":"right_to_left","supported_as_source":false,"supported_as_target":false,"identifiable":true}]
Update
you can test it using Console.WriteLine
foreach (var lg in languages)
{
Console.WriteLine($"Language Name: {lg.native_language_name}, Coutry Code: {lg.country_code}");
}
Language class
public class Language
{
public string language { get; set; }
public string language_name { get; set; }
public string native_language_name { get; set; }
public string country_code { get; set; }
public bool words_separated { get; set; }
public string direction { get; set; }
public bool supported_as_source { get; set; }
public bool supported_as_target { get; set; }
public bool identifiable { get; set; }
}
another classes (if needed)
public class Root
{
public int StatusCode { get; set; }
public Headers Headers { get; set; }
public Result Result { get; set; }
}
public class Headers
{
[JsonProperty("X-XSS-Protection")]
public string XXSSProtection { get; set; }
[JsonProperty("X-Content-Type-Options")]
public string XContentTypeOptions { get; set; }
[JsonProperty("Content-Security-Policy")]
public string ContentSecurityPolicy { get; set; }
[JsonProperty("Cache-Control")]
public string CacheControl { get; set; }
public string Pragma { get; set; }
[JsonProperty("Strict-Transport-Security")]
public string StrictTransportSecurity { get; set; }
[JsonProperty("x-dp-watson-tran-id")]
public string XDpWatsonTranId { get; set; }
[JsonProperty("X-Request-ID")]
public string XRequestID { get; set; }
[JsonProperty("x-global-transaction-id")]
public string XGlobalTransactionId { get; set; }
public string Server { get; set; }
[JsonProperty("X-EdgeConnect-MidMile-RTT")]
public string XEdgeConnectMidMileRTT { get; set; }
[JsonProperty("X-EdgeConnect-Origin-MEX-Latency")]
public string XEdgeConnectOriginMEXLatency { get; set; }
public string Date { get; set; }
public string Connection { get; set; }
}
public class Result
{
public List<Language> languages { get; set; }
}
````
Deserialize nested Json into a list
A simple approach is to use JArray
and iterate through the values in the DOM, extracting them as needed:
var parsed = JArray.Parse(json);
foreach (var item in parsed[0]["data"])
{
Console.WriteLine($"{item["lat"]} {item["lng"]} {item["fixTime"]}");
}
However, a more robust method is to deserialize into classes. I just pasted your json into https://json2csharp.com. It even suggests how to deserialize your json (although, since the json represents an array I had to update to deserialize to List<Root>
). This is the result (comments are mine):
// use this to deserialize
List<Root> myDeserializedClass = JsonConvert.DeserializeObject<List<Root>>(myJsonResponse);
// classes
// you'll need to define what goes here
public class Geozones
{
}
public class Datum
{
public string state { get; set; }
public double lat { get; set; }
public double lng { get; set; }
public int accuracy { get; set; }
public int fixTime { get; set; }
public string source { get; set; }
public Geozones geozones { get; set; }
public string address { get; set; }
public string type { get; set; }
public int id { get; set; }
public DateTime time { get; set; }
public DateTime insertTime { get; set; }
public int seqNbr { get; set; }
}
public class Root
{
public List<Datum> data { get; set; }
public bool truncated { get; set; }
public bool skipped { get; set; }
public string serial { get; set; }
public string name { get; set; }
public string type { get; set; }
}
Then to get the values:
foreach (var item in myDeserializedClass)
{
foreach (var data in item.data)
{
Console.WriteLine($"{data.lat} {data.lng} {data.fixTime}");
}
}
Alternatively, you could mix the two approaches and use ToObject<T>()
:
List<Datum> items = JArray.Parse(json)[0]["data"].ToObject<List<Datum>>();
Deserialize nested JSON, C#
There are a couple of problems here:
In your
Body
class, theTrackList
property does not match the JSON. The corresponding property in the JSON is calledtrack_list
. The class properties must either exactly match the JSON (ignoring case) or else you need to use a[JsonProperty]
attribute on the property to indicate what the JSON name will be. For example:public class Body
{
[JsonProperty("track_list")]
public List<TrackList> TrackList { get; set; }
}You are attempting to deserialize into the
Track
class, but you should be deserializing toRoot
since that represents the root of the JSON.var root = JsonConvert.DeserializeObject<Root>(json);
Once you have deserialized to
Root
you can "drill down" to print out the tracks.foreach (var item in root.message.body.TrackList)
{
Console.WriteLine(item.track.track_name);
}
Fiddle: https://dotnetfiddle.net/JnljGU
Related Topics
How to Compare (Directory) Paths in C#
Get All Controls of a Specific Type
Why Does "Int[] Is Uint[] == True" in C#
How to Invoke an Extension Method Using Reflection
Is the ++ Operator Thread Safe
Is There a Good Way to Convert Between Bitmapsource and Bitmap
How to Hide Tabpage from Tabcontrol
How to Save a List<String> on Settings.Default
Showing a Windows Form on a Secondary Monitor
Parameterized Queries VS. SQL Injection
Calling Async Methods from a Wcf Service
How to Drag and Move Shapes in C#
Smoothly Connecting Circle Centers
Prevent Using Dispatcher.Invoke in Wpf Code
Difference Between Parameters.Add(String, Object) and Parameters.Addwithvalue