How to Deserialize a Complex JSON Object in C# .Net

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.

  1. The near_earth_objects section is actually a dictionary of daily observations. Instead of creating a near_earth_objects class it's better to use a Dictionary<string,Observation[]>.
  2. 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:

  1. In your Body class, the TrackList property does not match the JSON. The corresponding property in the JSON is called track_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; }
    }
  2. You are attempting to deserialize into the Track class, but you should be deserializing to Root 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



Leave a reply



Submit