0

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;
       }

   }
}

4 Answers 4

1

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);
Sign up to request clarification or add additional context in comments.

1 Comment

That solved it. Thank you very much for your help!
0

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!!!

2 Comments

Thank you. I do not quite understand this just yet but will see if I can find out how I can use this and if it will be more efficient for my application. Thank you.
@Yijafe If you want to use these JSON values globally in your application then use my code otherwise use this code - serializer.Deserialize<ClassName>(jsonValue);
0

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; }

1 Comment

Thank you. Rather than edit the json file, I just referenced rootobject instead. Thank you. Also, regarding the class names, that is the way visual studio created them by default. I will see if there is a way to make them pascal case automatically. I just posted a small sample. There are quite a few more classes and JSON objects I removed so it will take a while to manually update them. Thank you.
0

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()));
}

1 Comment

Thank you very much! I understand what I was doing incorrectly now. Thank you.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.