Deserialize appsettings.json with json array into C# List - c#

I have a C# .Net Core Console Application thats supposed to check different Logfiles if these contain Errors and notifies the right person via Email. The Application has an appsetting.json that looks like this:
{
"Programs": [
{
"name": "program 1",
"LoggingPath": "C:\...",
"Emails": [
"person1#company.com",
"person2#company.com",
"person3#company.com"
]
},
{
"name": "program 2",
"LoggingPath": "C:\...",
"Emails": [
"person1#company.com",
"person2#company.com",
"person3#company.com"
]
}
]
}
Now i want to convert this Json into a C# Object List to iterate through the different Programs and do my Log analyzing etc.
I used https://json2csharp.com to convert the Json into this C# Code:
public class Program
{
public string name { get; set; }
public string LoggingPath { get; set; }
public List<string> Emails { get; set; }
}
public class Root
{
public List<Program> Programs { get; set; }
}
My code to initialize the appsettings.json and deserialization looks like this:
public static void Main(string[] args)
{
IConfiguration configuration;
configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
JToken JtokenConfig = Serialize(configuration);
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(JtokenConfig.ToString());
}
But when I try this I get the following Error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[LogAnalyzer.Model+Program]' 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.
I can't change the Json to an array (Without the "Programs":) cause the appsettings.json builder requires an object and not an array.
I have also tried to use this Json to get rid of the array and use a dynamic C# property to deserialize the different object names but the deserialization also failed.
{
"Programs": {
"program 1": {
"LoggingPath": "test",
"Emails": [
"person1#company.com",
"person2#company.com",
"person3#company.com"
]
},
"program 2": {
"LoggingPath": "test",
"Emails": [
"person1#company.com",
"person2#company.com",
"person3#company.com"
]
}
}
}
I read countless stackoverflow threads and other websites but wasnt able to convert the Json into a C# List.
I hope someone here can help me or give me a hint.

you can install Microsoft.Extensions.Configuration.Binder Nuget package and try this code
using Microsoft.Extensions.Configuration;
List<Program> programs = configuration.GetSection("Programs").Get<List<Program>>();
Update
the second json you can deserialize to a dictionary
Dictionary<string,Program> programsDict = configuration.GetSection("Programs")
.Get<Dictionary<string,Program>>();

Related

What is the best practice for receiving/handling json data in c#?

C#
Let's say that I want to make a winform page that receives a certain amount of random features read from the contents of a json file. (while using the 'Newtonsoft.Json' library)
Code that Deserializes the json file
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(File.ReadAllText(#"..\..\Json\features.json"));
These classes are set to represent the contents and structure of the json file within the same c# file, but outside of the main class of course.
class Root
{
public List<Category> category { get; set; }
}
class Category
{
public string name { get; set; }
public List<Feature> feature { get; set; }
}
class Feature
{
public string name { get; set; }
public string frequency { get; set; }
}
I have followed the practices to make this happen here:
https://www.newtonsoft.com/json/help/html/DeserializeWithJsonSerializerFromFile.htm
Can not deserialize JSON with array in array
https://json2csharp.com/
It works, however there are two main problems I can see with this:
It can end up visually cluttering the .cs file with lots of classes that you'll never need to look or edit, especially when it is interpreting multiple json files
It isn't dynamic, and it will only work for one json file that you have to make compatible by creating these classes for every subclass that exists within the json file.
My question: Is there a way to deserialize the json file without having to resort to creating multiple classes for every sub-category of data in the json string?
I believe you don't need the classes, because you don't need the whole JSON string, is that correct?
If so, instead of deserializing the whole json file, you could partially deserialize only the parts which you are interested in.
Have a look at this example from the Newtonsoft.Json documentation, where we have a long json string representing a response from a Google search, but are only interested in the responseData/results part of it, and only in some fields of that result object:
Object to partially desierialize:
public class SearchResult
{
public string Title { get; set; }
public string Content { get; set; }
public string Url { get; set; }
}
Deserializing Partial JSON Fragment Example:
string googleSearchText = #"{
'responseData': {
'results': [
{
'GsearchResultClass': 'GwebSearch',
'unescapedUrl': 'http://en.wikipedia.org/wiki/Paris_Hilton',
'url': 'http://en.wikipedia.org/wiki/Paris_Hilton',
'visibleUrl': 'en.wikipedia.org',
'cacheUrl': 'http://www.google.com/search?q=cache:TwrPfhd22hYJ:en.wikipedia.org',
'title': '<b>Paris Hilton</b> - Wikipedia, the free encyclopedia',
'titleNoFormatting': 'Paris Hilton - Wikipedia, the free encyclopedia',
'content': '[1] In 2006, she released her debut album...'
},
{
'GsearchResultClass': 'GwebSearch',
'unescapedUrl': 'http://www.imdb.com/name/nm0385296/',
'url': 'http://www.imdb.com/name/nm0385296/',
'visibleUrl': 'www.imdb.com',
'cacheUrl': 'http://www.google.com/search?q=cache:1i34KkqnsooJ:www.imdb.com',
'title': '<b>Paris Hilton</b>',
'titleNoFormatting': 'Paris Hilton',
'content': 'Self: Zoolander. Socialite <b>Paris Hilton</b>...'
}
],
'cursor': {
'pages': [
{
'start': '0',
'label': 1
},
{
'start': '4',
'label': 2
},
{
'start': '8',
'label': 3
},
{
'start': '12',
'label': 4
}
],
'estimatedResultCount': '59600000',
'currentPageIndex': 0,
'moreResultsUrl': 'http://www.google.com/search?oe=utf8&ie=utf8...'
}
},
'responseDetails': null,
'responseStatus': 200
}";
// Parse JSON into a JObject, which we can easily traverse
JObject googleSearch = JObject.Parse(googleSearchText);
// get JSON result objects into a list
IList<JToken> results = googleSearch["responseData"]["results"].Children().ToList();
// serialize JSON results into .NET objects
IList<SearchResult> searchResults = new List<SearchResult>();
foreach (JToken result in results)
{
// JToken.ToObject is a helper method that uses JsonSerializer internally
SearchResult searchResult = result.ToObject<SearchResult>();
searchResults.Add(searchResult);
}
// Title = <b>Paris Hilton</b> - Wikipedia, the free encyclopedia
// Content = [1] In 2006, she released her debut album...
// Url = http://en.wikipedia.org/wiki/Paris_Hilton
// Title = <b>Paris Hilton</b>
// Content = Self: Zoolander. Socialite <b>Paris Hilton</b>...
// Url = http://www.imdb.com/name/nm0385296/
This way, we only need to create a class for SearchResult and for nothing else, which sounds like what you want to have. While traversing the JSON object with code like googleSearch["responseData"]["results"] you can check whether the result is null and act accordingly, which means you can have optional fields in your JSON file, which are not present in other files, without your code breaking.
Does this help you solve your issues?
I believe that best practice is using classes.
Here are my arguments for using classes in your case:
You can read json bit by bit, use JObject.Parse or even dynamic (ugh) but it think your code should depend on a class (a dto) not on a json string. This input structure happens to be stored in json, but may not be. Most of your unit tests should take in an object, not a string.
I find the argument of deserialising to classes not being dynamic weak because you need to write the code that will handle the added elements. In other words if you add a new feature to json it won't just work, you need to write the code to support it and each time you change the json structure you need to update your code anyway.

Deserializes JSON to an object in c# using json.net

I am trying to convert my json into a rule object. I have been following this guide from Newtonsoft http://www.newtonsoft.com/json/help/html/deserializeobject.htm.
public class Rule{
public string Field { get; set; }
public string Test { get; set; }
public Rule[] Cases { get; set; }
}
public class Rules {
public List<Rule> Root{ get; set; }
}
My Json from rules.js
{
"Rules": [{
"Field": "Subject",
"Test": "^(Azure Exception)",
"Cases": [{
"Field": "Content",
"Test": "Hostname: az.....(?<Hostname>[^\n])",
"Cases": [{
"Field": "Content",
"Test": "Hostname:\\s+(?<Hostname>.*)\\s+Site name:\\s+(?<SiteName>.*)"
}]
}]
}]
}
In my main method:
String RulesFile = "cSharp/rules.js";
String Json = System.IO.File.ReadAllText(RulesFile);
Rule rule = JsonConvert.DeserializeObject<Rule>(Json);
var rules = JsonConvert.DeserializeObject<Rules>(Json);
//rule.Cases
//rule.Field
//rule.Test
//rules.Root
Console.Write(rule.Field);
I've tested my json and i can output it in my terminal. I'm unsure how to assign each field in json to my rules objects. Looking at the newtonsoft docs this should work, but I'm not getting any output.
I want to be able to print these fields out, anyone know to do it?
Cheers all in advance.
In your JSON string, the root object is an object with a Rules property. That property is an array of objects. You need to define and deserialize the root object, eg
class Rules
{
public Rule[] Rules{get;set;}
}
var rules = JsonConvert.DeserializeObject<Rules>(Json);
You can generate the DTOs requires for deserialization in Visual Studio by copying the JSON string and select Paste JSON as Classes in the Edit menu. You can also generate classes by using an online converter like json2csharp

Can't figure out how to store data from API call [duplicate]

This question already has answers here:
Converting JSON to Object fails - Cannot deserialize the current JSON object into System.Collections.Generic.List
(4 answers)
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1
(6 answers)
Cannot deserialize the current JSON object (e.g. {"name":"value"})
(6 answers)
JSON.Net - cannot deserialize the current json object (e.g. {"name":"value"}) into type 'system.collections.generic.list`1
(2 answers)
Closed 2 years ago.
This is my first time asking a question. I've been trying to get this working for too long. I've looked over some similar posts but just can't quite crack it.
I'm trying to make an API call in c# using flurl. I can make the call, but can't figure out how to store the data.
Here is an example of the response from the call made using powershell:
StatusCode : 200
StatusDescription : OK
Content : {
"result": [
{
"apiUserName": "admin#somedomain.com",
"apiPassword": "dk65dss5s5HH",
"id": "d38a1ea4-46d1-46b7-87a2-46c09da663eb",
"name": "catHotel",
"applUrl": "https://catshotel.somedomain.com/manager/",
"apiUrl": "https://catshotel.somedomain.com/API/",
"stateId": 2,
"databaseName": null,
"databaseServerName": null
},
{
"apiUserName": "admin#somedomain.com",
"apiPassword": "dhh7k33kDDh5g",
"id": "00d5e97b-5ea6-47dd-b920-8678f949c51f",
"name": "possumLodge",
"applUrl": "https://possumlodge.somedomain.com/manager/",
"apiUrl": "https://possumlodge.somedomain.com/API/",
"stateId": 1,
"databaseName": "customer-datab",
"databaseServerName": "customersDBserv.somedomain.com"
}
],
"targetUrl": null,
"success": true,
"error": null,
"unAuthorizedRequest": false,
"__abp": true
}
RawContent : HTTP/1.1 200 OK
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Request-Context: appId=cid-v1:8cgg58f2-5212-4685-b45f-149104d7a5be
Access-Control-Expose-Headers: Request-Context
X-Fr…
Headers : {[Cache-Control, System.String[]], [Request-Context, System.String[]], [Access-Control-Expose-Headers, System.String[]], [X-Frame-Options, System.String[]]…}
Images : {}
InputFields : {}
Links : {}
RawContentLength : 23638
RelationLink : {}
In c#, I've made a class: -
class Facilities
{
public string Name { get; set; }
}
And then I make the call: -
List<facilities> facilities = await url.WithHeaders(new { Accept = "Application/json", Authorization = token }).GetJsonAsync<List<facilities>>();
But I get errors such as:-
"Message "Cannot deserialize the current JSON object (e.g.
{"name":"value"}) into type
'System.Collections.Generic.List`1[ConsoleApp2.facilities]' because
the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.\r\nTo 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.\r\nPath 'result', line 1, position
10." string"
I can't figure out how to store each 'facility' into an array of facilities :(
I'm Not an Expert here and Can't say i know about flurl, so i looked here https://jonathancrozier.com/blog/calling-all-apis-how-to-use-flurl-with-c-sharp
and looked into the error message and as the message say, your strongly-typed model need to match the Json object, so that the deserialization mechanism in flurl can deserialize the Json objects that match your strongly-typed model properties, meaning your class Facilities should look something like this.
class Facilities
{
public string apiUserName { get; set; }
public string apiPassword { get; set; }
public int id { get; set; }
public string name { get; set; }
public string applUrl { get; set; }
public string apiUrl { get; set; }
public int stateId { get; set; }
public string databaseName { get; set; }
public string databaseServerName { get; set; }
}
And try using
var facilities = await url.WithHeaders....
so you can figure out what can of object you are getting.
Note: I mimicked your request code using the Package Flurl.Http v2.4.2, with this code
using Flurl;
using Flurl.Http;
var token = "SomeToken1234";
Url url = "https://jsonplaceholder.typicode.com/todos";
List<Todo> todos = await url.WithHeaders(new { Accept = "application/json", Authorization = token }).GetJsonAsync<List<Todo>>();
this here work, you can test it, maybe it's something regard the version you use.
You can use a System.Dynamic namespace
Then deserialize it like so:
var responseContent = response.Content;
var responseString = responseContent.ReadAsStringAsync().Result;
dynamic projects = JArray.Parse(responseString) as JArray;
the controller action needs to return VIEW(project)
lastly, your View page needs to use #model dynamic

Deserialize nested json to string property

I have a json file that contains nested json which I would like to deserialize into a string property upon configuration binding.
This is the configuration file:
{
"Service": {
"Id": "ccApiMicroservice",
"Configuration": {
"Option1": "value1",
"Option1": "value2"
}
}
}
And this is the corresponding configuration class:
public class ServiceOptions
{
public string Id { get; set; }
[JsonConverter(typeof(JsonStringConverter))]
public string Configuration { get; set; }
}
I have tried to use a custom json converter to convert the nested json to string, however the converter is ignored by the binding mechanism (have breakpoints in the converter's methods, but not one is hit), even though I have configured it in the ConfigureServices like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(
options => options.SerializerSettings.Converters.Add(new JsonStringConverter()));
services.Configure<ServiceOptions>(this.Configuration.GetSection("Service"));
}
What am I missing? Why is the converter ignored? Or is there another way how can I deserialize the nested json to string property?
Edit
The reason why I tried to use a custom JSON converter is that if I try to access the ServiceOptions instance anywhere in the application I get the following exception:
System.InvalidOperationException: 'Cannot create instance of type 'System.String' because it is missing a public parameterless constructor.'
Here is an example of a possibly easier way for you to get and parse your json items:
JObject config = JObject.Parse(File.ReadAllText("config.json"));
string service_id = (string)config.SelectToken("Service").SelectToken("Id");
Console.WriteLine(service_id);

Get specific json elements

I couln't find a similar case here, hence my question. I have a json like this:
{
"prop1": "bla",
"propn": "bla",
"Data": {
"42": {
"prop1": "bla",
"prop2": "bla",
"Symbol": "42"
},
"abc": {
"prop1": "bla",
"prop2": "bla",
"Symbol": "abc"
}
},
"Type": 100
}
Now, how do I get all elements from Data, and the most I am interested in the ones that have the symbol property set. I tried Newtonsoft.json.linq and jobject, but got really no clue what to do here. Any guidance anyone? Thanks!
Ronald
What you're looking for is called 'deserialize'. You have a string (the json in you post) and you want to turn it into an object.
The first steps you need to do are:
Create a class that matches your data.
Simply copy your json string in your post and use the option in Visual Studio to 'paste JSON as class'. Perhaps clean it up by changing the name RootObject to something more descriptive.
Install the NuGet package Newtonsoft in Visual Studio.
Now you can use MyClass myObject = JsonConvert.DeserializeObject<MyClass>(myString);
To access Symboljust use myObject.Data.Symbol
I imagine that once you extract partial data from json, if you still need to pass the data through your application, a dedicated model will come handy.
public class Data
{
public Element abc { get; set; }
}
public class Element
{
public string prop1 { get; set; }
public string prop2 { get; set; }
public string Symbol { get; set; }
}
While you certainly can rely on JObject handling the deserialization, i find it more intuitive to work with anonymous templates, especially for partial data retrieval.
var template = new
{
Data = default(Data)
};
var instance = JsonConvert.DeserializeAnonymousType(json, template);
will give you something like
I recomend you to use Jil library, is faster and more simple than Newtonsoft.json

Categories