Reputation: 3
EDIT: It is not related to the try/catch return null statement. I have debugged it and watched it and ensured it does not go to that catch block. I even commented it out, to no avail.
I am tiring to build a configuration manager for my application. Essentially I have certain variables that need to be stored in a JSON file and read during application startup so that certain values are known.
For some of these config variables, I'll allow the user to override the default, so if the "user" key has a non-empty string, I'll use that. If there is no user key or if there is an empty string, I'll use the default. To satisfy this requirement I wrote "AllowUserConfig" and "CoalesceUserDefault" methods to take care of that.
At this point, I have a JSON file (below). I copied the data and pasted into visual studio as a class, JSONConfig. I then have a ConfigManager file that does the real work and my program.cs calls the ConfigManager to use the DeserializeConfigVariables method to read the config file (which is in the correct location), and create a JSONConfig object from that.
With that being said, the JSONConfig object in DeserializeConfigVariables is being returned as null (on the line that says configVariables = serializer.Deserialize<JSONConfig>(jsonReader);
). Is there something I'm missing. I've gone over everything a hundred times and can't see what I'm doing incorrectly.
Any and all help would be appreciated.
This is my JSON:
{
"format": {
"date": {
"default": "yyyyMMdd",
"user": ""
},
"month_year": {
"default": "MM_YYYY",
"user": ""
}
},
"placeholders": {
"current_date": "{date}",
"month_year": "{month_year}",
"last_monday": "{prev_monday}",
"next_monday": "{next_monday}"
},
"resource_locations": {
"directories": {
"root_working": {
"default": "C:\\ALL",
"user": ""
},
"exports": {
"default": "C:\\ALL\\Exports",
"user": ""
},
"completed_exports": {
"default": "C:\\ALL\\Done",
"user": ""
},
"archived": {
"default": "C:\\ALL\\Archives",
"user": ""
}
},
"compression": {
"filename": {
"default": "{next_monday}_daily_{date}.zip",
"user": ""
}
},
"logging": {
"directory": "logs",
"process_filename": "activity_log_{month_year}.log",
"process_error_filename": "errors.log",
"system_error_filename": "sys_errors.log"
}
}
}
And this is the JSONConfig class I made by copying and pasting JSON as class in Visual Studio:
using System;
using Newtonsoft.Json;
namespace Config
{
public class JSONConfig
{
public RootObject ConfigVariables { get; set; }
}
public class RootObject
{
public Format format { get; set; }
public Placeholders placeholders { get; set; }
public Resource_Locations resource_locations { get; set; }
}
public class Format
{
public Date date { get; set; }
public Month_Year month_year { get; set; }
}
public class Date
{
public string _default { get; set; }
public string user { get; set; }
}
public class Month_Year
{
public string _default { get; set; }
public string user { get; set; }
}
public class Placeholders
{
public string current_date { get; set; }
public string month_year { get; set; }
public string last_monday { get; set; }
public string next_monday { get; set; }
}
public class Resource_Locations
{
public Directories directories { get; set; }
public Compression compression { get; set; }
public Logging logging { get; set; }
}
public class Directories
{
public Root_Working root_working { get; set; }
public Exports exports { get; set; }
public Completed_Exports completed_exports { get; set; }
public Archived archived { get; set; }
}
public class Root_Working
{
public string _default { get; set; }
public string user { get; set; }
}
public class Exports
{
public string _default { get; set; }
public string user { get; set; }
}
public class Completed_Exports
{
public string _default { get; set; }
public string user { get; set; }
}
public class Archived
{
public string _default { get; set; }
public string user { get; set; }
}
public class Compression
{
public Filename filename { get; set; }
}
public class Filename
{
public string _default { get; set; }
public string user { get; set; }
}
public class Logging
{
public string directory { get; set; }
public string process_filename { get; set; }
public string process_error_filename { get; set; }
public string system_error_filename { get; set; }
}
}
Then, in my ConfigManager files I have the following:
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using Newtonsoft.Json;
namespace Config
{
static class ConfigManager
{
// Assumes parent directory is bin. Assumes config is sibling to bin.
private static string _initialConfig = @"config\config.json";
public static string ConfigPath() => Path.Combine(GetRootAppDir(), _initialConfig);
public static string Parent(string directory)
{
string parentDirectory = Directory.GetParent(directory).FullName;
return parentDirectory;
}
public static string GetRootAppDir()
{
return Parent(Parent(Directory.GetCurrentDirectory()));
}
public static JSONConfig DeserializeConfigVariables()
{
try
{
var configVariables = new JSONConfig();
var serializer = new JsonSerializer();
Console.WriteLine($"Config Path: {ConfigPath()}"); //testing
using (var reader = new StreamReader(ConfigPath()))
using (var jsonReader = new JsonTextReader(reader))
{
configVariables = serializer.Deserialize<JSONConfig>(jsonReader);
}
return configVariables;
}
catch (System.IO.DirectoryNotFoundException ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
public static bool AllowUserConfig(object configVariable)
{
string userFieldName = "user";
var objType = configVariable.GetType();
return objType.GetMethod(userFieldName) != null;
}
public static string CoalesceUserDefault(dynamic configVariable)
{
if (AllowUserConfig(configVariable))
{
if (!(String.IsNullOrEmpty(configVariable.user)))
{
return configVariable.user;
}
}
return configVariable._default;
}
}
}
Upvotes: 0
Views: 243
Reputation: 11364
You need to deserialize your json to RootObject
, not JSONConfig
configVariables = serializer.Deserialize<RootObject>(jsonReader);
Your json has three root objects, Format
, Placeholders
and ResourceLocations
... but the class you used to deserialize the json to has only one object.. and doesnt match up.
You can always use www.json2csharp.com, paste your json there and see which object you need to deserialize to (base is always RootObject there).
How to use JSONConfig class
If you really want to use JSONConfig to deserialize your json, then you will need to modify your JSON a bit. You will need to add another root element ConfigVariables
to the json.
{ "ConfigVariables" :
{
"format": {
"date": {
"default": "yyyyMMdd",
"user": ""
...
Recommendation
Change the method that deserializes your JSON to a simpler process as well.
public static RootObject DeserializeConfigVariables()
{
return JsonConvert.DeserializeObject<RootObject>(File.ReadAllLines(ConfigPath()));
}
Upvotes: 0
Reputation: 18155
Your Json doesn't contain a property called ConfigVariable
at the root as defined in the class JSONConfig
(the type to which you are attempting to deserialize).
The Json, if you inspect, suits the definition of RootObject
. You should deserialize your class to an instance of RootObject.You could assign it to the ConfigVariable
property of instance of JsonConfig
if your intention is to store the configuration in instance of JsonConfig.
configVariables.ConfigVariables = serializer.Deserialize<RootObject>(jsonReader);
Upvotes: 1
Reputation: 387
Your Json file does not correspond to object structure which you are using to deserealize it.
Your JsonConfig
file contains ConfigVariables
property but json file contains format
, placeholders
, resource_locations
proerties which should be on second level.
Try to update your config file following way:
{
"ConfigVariables": {
"format": {
"date": {
"default": "yyyyMMdd",
"user": ""
},
"month_year": {
"default": "MM_YYYY",
"user": ""
}
},
"placeholders": {
"current_date": "{date}",
"month_year": "{month_year}",
"last_monday": "{prev_monday}",
"next_monday": "{next_monday}"
},
"resource_locations": {
"directories": {
"root_working": {
"default": "C:\\ALL",
"user": ""
},
"exports": {
"default": "C:\\ALL\\Exports",
"user": ""
},
"completed_exports": {
"default": "C:\\ALL\\Done",
"user": ""
},
"archived": {
"default": "C:\\ALL\\Archives",
"user": ""
}
},
"compression": {
"filename": {
"default": "{next_monday}_daily_{date}.zip",
"user": ""
}
},
"logging": {
"directory": "logs",
"process_filename": "activity_log_{month_year}.log",
"process_error_filename": "errors.log",
"system_error_filename": "sys_errors.log"
}
}
}
}
It will solve your problem.
By the way, as I see you've named properties in your configuration objects in json style. E.g. resource_locations
it's better to name properties in regular way and add JsonProperty
attribute for correct mapping.
e.g.
[JsonProperty('resource_locations')]
public ResourceLocations ResouceLocations { get; set; }
Upvotes: 0
Reputation: 1405
Hope, it helps you.
You can add yourjsonfile.json
file
e.g.:-
yourjsonfile.json
{
"key1": "value1",
"key2": "value2",
"ConnectionStrings": {
"$ref": "anotherjsonfile.json#"
}
}
anotherjsonfile.json
{
"key2": "value4"
}
Make sure that yourjsonfile.json and anotherjsonfile.json files property set "Copy To Output Directory" to "Copy always".
Find Values from yourjsonfile.json
and anotherjsonfile.json files like this-
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
IConfiguration configuration = builder.Build();
string GetValue1 = configuration.GetSection("key1").Value;
string GetValue2 = configuration.GetSection("key2").Value;
var builder1 = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(configuration.GetConnectionString("$ref").Replace("#", ""), optional: true, reloadOnChange: true);
IConfiguration configuration1 = builder1.Build();
GetValue2 = (configuration1.GetSection("key2").Value) != null ? configuration1.GetSection("key2").Value : GetValue2;
Thanks!!!
Upvotes: 0