Trying to deserialize a complex json file in c# - c#

I'm trying to deserialize a json file in c# and print the values of each individual value by using the key, but for the json values that are a list, I'm unable to print the desired values.
Json file:
{
"prod": {
"vpc_name": "vpc-us-east-1-it-prod",
"subnets": {
"application": [ "subnet-1234", "subnet-1345" ],
"data": [ "subnet-64t3", "subnet-f321" ],
"edge": [ "subnet-1ff3", "subnet-134g" ]
}
},
"dev": {
"vpc_name": "vpc-us-east-1-it-dev",
"subnets": {
"application": [
{ "subnet_id": "subnet-2345tf", "az": "us-east-1a" },
{ "subnet_id": "subnet-143f1", "az": "us-east-1b" }
],
"data": [
{ "subnet_id": "subnet-134f", "az": "us-east-1b" },
{ "subnet_id": "subnet-1324", "az": "us-east-1a" }
],
"edge": [
{ "subnet_id": "subnet-123434", "az": "us-east-1a" },
{ "subnet_id": "subnet-123fg", "az": "us-east-1b" }
]
}
}
}
C# code
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace JsonDeserializer
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
string jsonString = File.ReadAllText(#"C:/Users/Stephen.Carvalho/source\repos/JsonDeserializer/JsonDeserializer/vpc_stack.json");
Rootobject vpcs = JsonSerializer.Deserialize<Rootobject>(jsonString);
Console.WriteLine(vpcs.dev.vpc_name);
Console.WriteLine(vpcs.dev.subnets.application);
}
}
public class Rootobject
{
public Prod prod { get; set; }
public Dev dev { get; set; }
}
public class Prod
{
public string vpc_name { get; set; }
public Subnets subnets { get; set; }
}
public class Subnets
{
public string[] application { get; set; }
public string[] data { get; set; }
public string[] edge { get; set; }
}
public class Dev
{
public string vpc_name { get; set; }
public Subnets1 subnets { get; set; }
}
public class Subnets1
{
public Application[] application { get; set; }
public Datum[] data { get; set; }
public Edge[] edge { get; set; }
}
public class Application
{
public string subnet_id { get; set; }
public string az0885b3b66d { get; set; }
public string az { get; set; }
}
public class Datum
{
public string subnet_id { get; set; }
public string az { get; set; }
}
public class Edge
{
public string subnet_id { get; set; }
public string az { get; set; }
}
}
Output
Hello World!
vpc-us-east-1-it-dev
JsonDeserializer.Application[]
I want to print the list of application subnets with prod.subnets but instead of getting the values returned I'm getting JsonDeserializer.Application[] as the output

That is because vpcs.dev.subnets.application is an array of Application. You can print the array of application subnets by first getting the subnet_ids then converting that array to a string.
Reason you see JsonDeserializer.Application[] is because your printing the object itself.. when you do that, you get the name of the object type instead of the values inside of it.
Console.WriteLine(string.Join(", ", vpcs.dev.subnets.application.Select(x => x.subnet_id)));
The Select statment goes through each of the application and returns only the subnet_id. Whenever you have an array / list, you can use the select method to get specific items from the list or create new object with this as well.
Documentation on Select

That's happening since you're trying to print an array in an line, you can try doing something like this:
Array.ForEach(vpcs.dev.subnets.application, Console.WriteLine);
or parsing it to a list and printing it
vpcs.dev.subnets.application.ToList().ForEach(i => Console.WriteLine(i.ToString()));
or you can also try doing it in a foreach:
foreach(var item in vpcs.dev.subnets.application)
{
Console.WriteLine(item.ToString());
}

Related

I can't deserialize the JSON

I'm trying to connect to the PostNL API Timeframes. The problem is that I keep getting the following exception. I think I got my models correctly but I can't seem to find out what's wrong with it.
Exception:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[PostNL.Api.Dtos.TimeFrameHolderDto]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'Timeframes.Timeframe', line 3, position 16.
at PostNL.Api.Tests.Unit.Tests.Unit.TimeFramesTests.TimeFrameResponseJsonTests.JsonConvertTests(String filePath) in C:\Git\PostNL.Api.Tests.Unit\TimeFramesTests\TimeFrameResponseJsonTests.cs:line 37
If I read the error I need to create some more models with holders and lists like I did as below but I'm still getting this exception. Does anyone know what I'm doing wrong?
requests/1.json file:
{
"Timeframes": {
"Timeframe": [
{
"Date": "22-08-2022",
"Timeframes": {
"TimeframeTimeFrame": [
{
"From": "08:45:00",
"Options": {
"string": "Daytime"
},
"To": "11:15:00"
},
{
"From": "17:30:00",
"Options": {
"string": "Evening"
},
"To": "22:00:00"
}
]
}
},
{
"Date": "23-08-2022",
"Timeframes": {
"TimeframeTimeFrame": [
{
"From": "09:15:00",
"Options": {
"string": "Daytime"
},
"To": "11:45:00"
},
{
"From": "17:30:00",
"Options": {
"string": "Evening"
},
"To": "22:00:00"
}
]
}
}
]
}
}
The deserialization test:
using PostNL.Api.Dtos;
using Newtonsoft.Json;
using NUnit.Framework;
using System;
using System.IO;
namespace PostNL.Api.Tests.Unit.TimeFramesTests
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
internal class TimeFrameResponseJsonTests
{
[Test]
[TestCase("../../../TimeFramesTests/requests/1.json")]
public void JsonConvertTests(string filePath)
{
var path = AppDomain.CurrentDomain.BaseDirectory;
var fullFilePath = Path.Combine(path, filePath);
if (!File.Exists(fullFilePath))
{
Assert.Fail("File does not exists");
return;
}
try
{
var fileContent = File.ReadAllText(filePath);
var json = JsonConvert.DeserializeObject<TimeFrameResponseDto>(fileContent);
}
catch (Exception exception)
{
Assert.Fail(exception.Message);
}
Assert.Pass("Successful convert");
}
}
}
The models:
using Newtonsoft.Json;
using System.Collections.Generic;
namespace PostNL.Api.Dtos
{
internal class TimeFrameDto
{
[JsonProperty("From")]
public string From { get; set; }
[JsonProperty("Options")]
public OptionDto[] Options { get; set; }
[JsonProperty("To")]
public string To { get; set; }
}
internal class TimeFrameTimeFrameHolder
{
[JsonProperty("TimeframeTimeFrame")]
public List<TimeFrameDto> TimeFrameTimeFrame { get; set; }
}
internal class DayTimeFrameDto
{
[JsonProperty("Date")]
public string Date { get; set; }
[JsonProperty("Timeframes")]
public List<TimeFrameTimeFrameHolder> TimeFrameDtos { get; set; }
}
internal class TimeFrameHolderDto
{
[JsonProperty("Timeframe")]
public List<DayTimeFrameDto> TimeFrames { get; set; }
}
internal class TimeFrameResponseDto : PostNLBaseDto
{
[JsonProperty("Timeframes")]
public List<TimeFrameHolderDto> TimeFrames { get; set; }
//[JsonProperty("ReasonNoTimeframes")]
//public List<ReasonNoTimeFrameDto> ReasonNoTimeFrames { get; set; }
}
}
you need one more class - DataTimeframesDto
TimeFramesResponseDTo timeFramesResponseDTo=JsonConvert.DeserializeObject<TimeFramesResponseDTo>(fileContent);
public partial class TimeFramesResponseDTo
{
[JsonProperty("Timeframes")]
public DataTimeframesDto Timeframes { get; set; }
}
public partial class DataTimeframesDto
{
[JsonProperty("Timeframe")]
public List<DayTimeFrameDto> Timeframe { get; set; }
}
UPDATE
The rest of my classes it you still can't use yours
public partial class DayTimeFrameDto
{
[JsonProperty("Date")]
public string Date { get; set; }
[JsonProperty("Timeframes")]
public TimeFrameTimeFrameHolder Timeframes { get; set; }
}
public partial class TimeFrameTimeFrameHolder
{
[JsonProperty("TimeframeTimeFrame")]
public List<TimeframeDTo> TimeFrameTimeFrame{ get; set; }
}
public partial class TimeframeDTo
{
[JsonProperty("From")]
public DateTime From { get; set; }
[JsonProperty("Options")]
public OptionsDto Options { get; set; }
[JsonProperty("To")]
public DateTime To { get; set; }
}
public partial class OptionsDto
{
[JsonProperty("string")]
public string OptionsString { get; set; }
}

Parse (Deserialize) JSON with dynamic keys (C#)

I want to parse the JSON on the bottom. Till now I always have the static key for the variables, but in this example, the keys are always changing. Here the "58e7a898dae4c" and "591ab00722c9f" could have any value and change constantly. How can I get the value of the elements of the set to be able to reach the PreviewName value?
{
"key": "gun",
"objects": [
{
"name": "AK47",
"sets": {
"58e7a898dae4c": {
"set_id": "58e75660719a9f513d807c3a",
"preview": {
"resource": {
"preview_name": "preview.040914"
}
}
},
"591ab00722c9f": {
"set_id": "58eba618719a9fa36f881403",
"preview": {
"resource": {
"preview_name": "preview.81a54c"
}
}
}
}
}
]
}
public class Object
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("sets")]
public Dictionary<string, Set> Sets { get; set; }
}
public class Set
{
[JsonProperty("set_id")]
public string SetId { get; set; }
[JsonProperty("preview")]
public Preview Preview { get; set; }
}
public class Preview
{
[JsonProperty("resource")]
public ResourcePreview Resource { get; set; }
}
public class ResourcePreview
{
[JsonProperty("preview_name")]
public string PreviewName { get; set; }
}
var root = JsonConvert.DeserializeObject<RootObject>(json);
string previewName1 = root.Objects[0].Sets["58e7a898dae4c"].Preview.Resource.PreviewName;
string previewName2 = root.Objects[0].Sets["591ab00722c9f"].Preview.Resource.PreviewName;
you don't need to deserialize, you can parse
var jsonParsed=JObject.Parse(json);
string[] previewNames= ((JArray)jsonParsed["objects"])
.Select(v => ((JObject)v["sets"]))
.Select(i=>i.Properties().Select(y=> y.Value["preview"]["resource"])).First()
.Select(i=> (string) ((JObject)i)["preview_name"]).ToArray();
result
preview.040914
preview.81a54c

Why is my JSON model not parsing not working for List of string only?

I have the following JSON data structure stored in a file, I have made a JSON data model in C# using the following tool JsonToCsharp. Usually, this tool is perfect and makes me awesome data models, but this time, for an unknown reason, each time I parse the JSON content, all string lists are null.
{
"Targets": [
{
"Name": "myTarget",
"Sharpmakes": [
{
"Name": "myTarget_v01",
"Dest": "/myTarget/folder/destination"
}
],
"Includes": [
"default_files" // <= This will always be null
]
},
{
"Name": "default_files",
"Directories": [
{
"Source": "/default/utils",
"Dest": "/utils",
"Includes": [ "*.bat", "*.ini", "*.txt", "*.xml", "*.json" ] // <= This will always be null
},
],
},
],
}
This is the code I am using for parsing my JSON:
try
{
var jsonContent = System.IO.File.ReadAllText(packageDefinitionJsonConfigPath);
return JsonConvert.DeserializeObject<Package>(jsonContent);
}
catch (Exception exception)
{
Log.Error($"Could not parse the json \n\n{packageDefinitionJsonConfigPath}");
throw exception;
}
Nothing is quite special about this code snippet, it's a simple NewtonSoft JSON parse.
And here are my generated models that JsonToCsharp gave me, (which looks just fine)...
[JsonObject]
public class Package
{
[JsonProperty("Targets")]
public List<Target> Targets { get; set; }
}
public class Sharpmake
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Excludes")]
public IList<string> Excludes { get; set; }
[JsonProperty("Dest")]
public string Dest { get; set; }
[JsonProperty("Includes")]
public IList<string> Includes { get; set; }
}
public class File
{
[JsonProperty("Source")]
public string Source { get; set; }
[JsonProperty("Dest")]
public string Dest { get; set; }
}
public class Directory
{
[JsonProperty("Source")]
public string Source { get; set; }
[JsonProperty("Dest")]
public string Dest { get; set; }
[JsonProperty("Includes")]
public IList<string> Includes { get; set; }
}
public class Target
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Sharpmakes")]
public IList<Sharpmake> Sharpmakes { get; set; }
[JsonProperty("Includes")]
public IList<string> Includes { get; set; }
[JsonProperty("Files")]
public IList<File> Files { get; set; }
[JsonProperty("Directories")]
public IList<Directory> Directories { get; set; }
}
public class RootObject
{
[JsonProperty("Targets")]
public IList<Target> Targets { get; set; }
}
Does anyone have an idea why my model could work just fine for everything except for my string lists? There nothing out of the extraordinary in this code snippet, so I'm really clueless here...
Updating my package to the latest version of Json.Net (12.0.2) seems to have fixed the issue
That would match with the release notes fixes
https://github.com/JamesNK/Newtonsoft.Json/releases/tag/12.0.2

C# Parse JSON response (Get a specific part from response)

I am trying to get a specific part from a JSON response string.
Here is the JSON code :
{
"metadata": {
"provider": "Oxford University Press"
},
"results": [
{
"id": "door",
"language": "en",
"lexicalEntries": [
{
"entries": [
{
"homographNumber": "000",
"senses": [
{
"definitions": [
"a hinged, sliding, or revolving barrier at the entrance to a building, room, or vehicle, or in the framework of a cupboard"
],
"id": "m_en_gbus0290920.005",
"subsenses": [
{
"definitions": [
"a doorway"
],
"id": "m_en_gbus0290920.008"
},
{
"definitions": [
"used to refer to the distance from one building in a row to another"
],
"id": "m_en_gbus0290920.009"
}
]
}
]
}
],
"language": "en",
"lexicalCategory": "Noun",
"text": "door"
}
],
"type": "headword",
"word": "door"
}
]
}
I am trying to get this code
"definitions": [
"a hinged, sliding, or revolving barrier at the entrance to a building, room, or vehicle, or in the framework of a cupboard"
in a string
Here is my code:
string language = "en";
string word_id = textBox1.Text.ToLower();
String url = "https://od-api.oxforddictionaries.com:443/api/v1/entries/" + language + "/" + word_id+"/definitions";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Add("app_id", app_Id);
client.DefaultRequestHeaders.Add("app_key", app_Key);
HttpResponseMessage response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
var s = JsonConvert.DeserializeObject(result);
textBox2.Text = s.ToString();
}
else MessageBox.Show(response.ToString());
I am using C#.
C# Classes
Step one is to create some classes to allow us to represent the data in C#. If you don't have them... QuickType does that.
namespace QuickType
{
using System;
using System.Net;
using System.Collections.Generic;
using Newtonsoft.Json;
public partial class GettingStarted
{
[JsonProperty("metadata")]
public Metadata Metadata { get; set; }
[JsonProperty("results")]
public Result[] Results { get; set; }
}
public partial class Result
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("lexicalEntries")]
public LexicalEntry[] LexicalEntries { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("word")]
public string Word { get; set; }
}
public partial class LexicalEntry
{
[JsonProperty("entries")]
public Entry[] Entries { get; set; }
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("lexicalCategory")]
public string LexicalCategory { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
}
public partial class Entry
{
[JsonProperty("homographNumber")]
public string HomographNumber { get; set; }
[JsonProperty("senses")]
public Sense[] Senses { get; set; }
}
public partial class Sense
{
[JsonProperty("definitions")]
public string[] Definitions { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("subsenses")]
public Subsense[] Subsenses { get; set; }
}
public partial class Subsense
{
[JsonProperty("definitions")]
public string[] Definitions { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
}
public partial class Metadata
{
[JsonProperty("provider")]
public string Provider { get; set; }
}
public partial class GettingStarted
{
public static GettingStarted FromJson(string json) => JsonConvert.DeserializeObject<GettingStarted>(json, Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this GettingStarted self) => JsonConvert.SerializeObject(self, Converter.Settings);
}
public class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
}
}
Deserialize
You'll notice that we also have converters to serialize and deserialize generated for us. The deserialize bit is as simple as:
var result = JsonConvert.DeserializeObject<GettingStarted>(json);
Use
Start from the result variable and use dots to find your way down to your item...
var description = result.results.lexicalEntries.First()
.entries.First()
.senses.First()
.definitions.First();
All of those First() calls are due to each of these parts of the data being arrays. You'll need to reference System.Linq for that. You will want to read a little around what to do if you have more than one, or less than one, at any of those levels (you may need to work with collections, or perform more traversal).
You can create a class whose properties are the names of the JSON you are trying to parse. That way you can deserialize the JSON into an instance of that class and pull whatever property you need. You'll need to use the Newtonsoft.Json package.
Example class:
public class YourClass
{
public string propertyA { get; set; }
public string propertyB { get; set; }
}
And then in your main code:
YourClass yourClass = new YourClass();
try
{
yourClass = JsonConvert.DeserializeObject<YourClass>(yourJsonStringGoesHere);
}
catch (Exception ex)
{
//log exception here
}

Consuming JSON in C# from mailchimp campaignEmailStatsAIMAll

I am trying to program .net c# code to read the results from a call to campaignEmailStatsAIMAll().
Here is a sample of the JSON results I get:
{
"total":3,
"data":{
"some...#company.com":[
{
"action":"click",
"timestamp":"2012-10-18 20:55:52",
"url":"http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)",
"ip":"66.66.666.666"
},
{
"action":"open",
"timestamp":"2012-10-18 20:55:52",
"url":null,
"ip":"66.66.666.666"
}
],
"anothe...#corporation.com":[
{
"action":"open",
"timestamp":"2012-10-18 20:18:05",
"url":null,
"ip":"22.222.222.222"
},
{
"action":"open",
"timestamp":"2012-10-18 20:18:18",
"url":null,
"ip":"22.222.222.222"
},
{
"action":"click",
"timestamp":"2012-10-18 20:18:18",
"url":"http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)",
"ip":"22.222.222.222"
},
{
"action":"click",
"timestamp":"2012-10-18 20:21:57",
"url":"http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)",
"ip":"22.222.222.222"
}
],
"thir...#business.com":[
]
}
}
The problem I am having is how to define the C# object to accept this JSON string. The email addresses are being treated as the name part of a JSON name/value pair. I know there are 3rd party wrappers for .NET but I only want to call this API (maybe a couple of others down the line) and would prefer to code it myself. I want to be able to iterate through the object to extract the email address then action/timestamp/url for each one.
When I drop this JSON into the object creator at json2sharp.com, I get this:
public class SomebodyCompanyCom {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class AnotherpersonCorporationCom {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class Data {
public List<SomebodyCompanyCom> __invalid_name__somebody#company.com { get; set; }
public List<AnotherpersonCorporationCom> __invalid_name__anotherperson#corporation.com { get; set; }
public List<object> __invalid_name__thirdguy#business.com { get; set; }
}
public class RootObject {
public int total { get; set; }
public Data data { get; set; }
}
A generator like that can only ever make best-guesses. Since JSON is generally schema-less, you need to analyze the data and figure out the best way to model it.
In your case, you have two basic types:
the individual entries consisting of action, timestamp, url, and ip
the overall container with total and data
A reasonable model might look like this:
public class ItemInfo {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class ContainerType {
public int total;
public Dictionary<string, ItemInfo[]> data;
}
If you use a decent JSON library like JSON.Net, you could deserialize the JSON string into an ContatinerType structure like so:
string rawJsonString = MagicallyGetTheJsonFromAWebservice();
ContainerType container = JsonConvert.DeserializeObject<ContainerType>(rawJsonString);
It might need a bit of tweaking to get the type deserialization to work exactly, but that's the basic idea.
Edit: sample console program to do the deserialization:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
namespace ConsoleApplication1
{
class Program
{
static string testData = #"{
'total':3,
'data':{
'some...#company.com':[
{
'action':'click',
'timestamp':'2012-10-18 20:55:52',
'url':'http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)',
'ip':'66.66.666.666'
},
{
'action':'open',
'timestamp':'2012-10-18 20:55:52',
'url':null,
'ip':'66.66.666.666'
}
],
'anothe...#corporation.com':[
{
'action':'open',
'timestamp':'2012-10-18 20:18:05',
'url':null,
'ip':'22.222.222.222'
},
{
'action':'open',
'timestamp':'2012-10-18 20:18:18',
'url':null,
'ip':'22.222.222.222'
},
{
'action':'click',
'timestamp':'2012-10-18 20:18:18',
'url':'http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)',
'ip':'22.222.222.222'
},
{
'action':'click',
'timestamp':'2012-10-18 20:21:57',
'url':'http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)',
'ip':'22.222.222.222'
}
],
'thir...#business.com':[
]
}
}";
public class ItemInfo {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class ContainerType {
public int total;
public Dictionary<string, ItemInfo[]> data;
}
static void Main(string[] args)
{
ContainerType container = JsonConvert.DeserializeObject<ContainerType>(testData);
if((container.total != 3)
|| (container.data.Count != 3)
|| (container.data["some...#company.com"][1].action != "open")
|| (container.data["anothe...#corporation.com"][2].url != "http://www.someurl.com?ct=t(First_Chimp_Test10_18_2012)"))
{
Console.WriteLine("Failed to deserialize");
}
else
{
Console.WriteLine("Successfully deserialized");
}
foreach (string email in container.data.Keys)
{
Console.WriteLine(email);
for (int x = 0; x < container.data[email].Count(); x++)
{
Console.WriteLine("\t" + x.ToString() + ":");
Console.WriteLine("\t\taction : " + container.data[email][x].action);
Console.WriteLine("\t\ttimestamp: " + container.data[email][x].timestamp);
Console.WriteLine("\t\turl : " + container.data[email][x].url);
Console.WriteLine("\t\tip : " + container.data[email][x].ip);
}
}
}
}
}

Categories