Reputation: 65
Background:
I am creating an avaloniaUI project and in it I have a json config with a class that serializes and deserializes it on initialization and editing. If the file can't be found on initialization the class makes a new one with a list of default properties. This class is supposed to be the only part of the code that can do that.
Problem:
For some reason the config file, when it does not exist already, is created before the constructor call of the config serializer class. That lead my friends and I to think that the file was being created somewhere else in the project. That can't be the case though because I've used a control f tool to comb the entire project for references of any filestreams, create commands, or a reference to the config path and I found nothing else that's capable of generating a file other than the code that's supposed to create the config but never runs. So what happens is the file is created empty before the code that's supposed to handle generation is called thus skipping the proper generation code which leads to json loading an empty string when the file is deserialized and it creates null value exceptions.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Newtonsoft.Json;
namespace RefOrganizerAvalonia.Util
{
public class ConfigSerializer
{
public List<ConfigItem> Items { get; }
private string _configPath;
private static List<ConfigItem> BuildConfig()
{
return new List<ConfigItem>()
{
new ConfigItem("PageResultSize", 50, typeof(int))
,new ConfigItem("ResultsPerLine", 5, typeof(int))
};
}
private List<ConfigItem> LoadConfig()
{
using (FileStream fs = new FileStream(_configPath, FileMode.Open))
{
var sr = new StreamReader(fs);
var dataStr = sr.ReadToEnd();
return JsonConvert.DeserializeObject(dataStr) as List<ConfigItem>;
}
}
public ConfigSerializer()
{ //break point here reveals that even before any of the constructor code is executed the
//file is created from an unknown location
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
_configPath = Path.Join(path, "ref_organizer_config.json");
Debug.WriteLine(_configPath);
Debug.WriteLine(File.Exists(_configPath));
if (!File.Exists(_configPath)) //never accessed because the (empty) file already
//exists by this point despite no code that can create
//it being executed
{
Debug.WriteLine("Config Not Found");
//Save BuildConfig() results in json string and write to file
Items = BuildConfig();
SaveConfig();
}
else
{
Items = LoadConfig();
}
}
private void SaveConfig()
{
using (FileStream fs = new FileStream(_configPath, FileMode.OpenOrCreate))
{
Debug.WriteLine("Save Config Triggered");
var data = JsonConvert.SerializeObject(Items);
Debug.WriteLine($"Serialized Config: {data}");
var sw = new StreamWriter(fs);
sw.Write(data);
}
}
}
}
Tests Performed:
Result: Empty file with config name and path is still created right before the initialization of config serializer.
_configPath
variable.
Result: Empty file created under new name.
SaveConfig()
function to private.
Result: No change.
Note:
If you need additional context of the avalonia project code for whatever reason send me a comment and I will post it ASAP.
Upvotes: 1
Views: 630
Reputation: 9479
In my small tests tracing your code, it appears the problem lies in the SaveConfig()
method. Walking through the code should reveal that the line of code… sw.Write(data);
… is executing, however, nothing is actually written to the file. Adding a Flush
statement for the StreamWriter
should ensure that… sw.Write(data);
… gets done. Adding the Flush()
command to the writer appears to actually write the data to the file.
private void SaveConfig() {
using (FileStream fs = new FileStream(_configPath, FileMode.OpenOrCreate)) {
Debug.WriteLine("Save Config Triggered");
var data = JsonConvert.SerializeObject(Items);
Debug.WriteLine($"Serialized Config: {data}");
var sw = new StreamWriter(fs);
sw.Write(data);
sw.Flush();
}
}
Edit... StreamWriter
should be wrapped in a using
statement.
Further tests reveal that wrapping a Using
statement around the StreamWriter
is needed and should be implemented. Therefore, wrapping the StreamWriter
in a using
statement should Flush
the data for you. Below is a better approach with the using
statement.
private void SaveConfig() {
using (FileStream fs = new FileStream(_configPath, FileMode.OpenOrCreate)) {
Debug.WriteLine("Save Config Triggered");
var data = JsonConvert.SerializeObject(Items);
Debug.WriteLine($"Serialized Config: {data}");
using (var sw = new StreamWriter(fs)) {
sw.Write(data);
}
}
}
Upvotes: 1