I am writing a class to serialize/deserialize a class which includes a DataTable to a JSON file using JSON.net using C# .NET CORE v5 Visual Studio 2019. When the following conditions occur, the write operation doesn't overwrite, instead it appends the DataTable to the file.
Note: I've stripped out all the error handling and support functions to reduce the size of the code and then placed into a console application.
What I've tried...
When I run the code below, I expect a JSON file with the following contents.
{
"Name": "Test Name",
"dt": [
{
"Name": "Buckaroo Banzai",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Hoban Washburne",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Dr. Morbius",
"Num": 1,
"Test,typeof(string)": "abcd"
}
]
}
Instead I get this file, note that the DataTable is duplicated twice.
{
"Name": "Test Name",
"dt": [
{
"Name": "Buckaroo Banzai",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Hoban Washburne",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Dr. Morbius",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Buckaroo Banzai",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Hoban Washburne",
"Num": 1,
"Test,typeof(string)": "abcd"
},
{
"Name": "Dr. Morbius",
"Num": 1,
"Test,typeof(string)": "abcd"
}
]
}
public static class FileOperations
{
private static FileStream fs = null;
private static string currentPath = null;
private static bool OpenFileStream(string path)
{
fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
currentPath = path;
return true;
}
public static bool ReadJSONFile<T>(string path, out T returnedInstanceOfClassFromJSONFile)
{
returnedInstanceOfClassFromJSONFile = default(T); //set to null
bool bSuccessOpenFileStream = true;
if (path != currentPath)
{
CloseFileStream();
bSuccessOpenFileStream = OpenFileStream(path);
}
if (bSuccessOpenFileStream)
{
using (StreamReader reader = new StreamReader(fs, Encoding.UTF8, true, 4096, leaveOpen: true))
using (JsonReader jreader = new JsonTextReader(reader))
{
fs.Seek(0, SeekOrigin.Begin);
JsonSerializer serializer = new JsonSerializer();
returnedInstanceOfClassFromJSONFile = (T)serializer.Deserialize(jreader, typeof(T));
}
return true;
}
return false;
}
public static bool WriteJSONFile(string path, object objectToSerialize)
{
CloseFileStream();
//I first tried using the streamwrite, but it has the same problem for example
//using (StreamWriter sw = new StreamWriter(fs, ....)
//using (JsonWriter writer = new JsonTextWriter(sw))
//{
// serializer.Serialize(writer, objectToSerialize);
//}
File.WriteAllText(path, JsonConvert.SerializeObject(objectToSerialize));
bool bSuccessOpenFileStream = OpenFileStream(path);
return bSuccessOpenFileStream;
}
private static void CloseFileStream()
{
if (fs != null)
{
fs.Close();
fs = null;
//shouldn't be requried, trying out of desperation
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
Using the following code. Note that if you comment out the ReadJSONFile, the problem doesn't occur.
static void Main(string[] args)
{
Simple simple = new Simple();
string path = @"c:\somepath";
FileOperations.WriteJSONFile(path, simple);
FileOperations.ReadJSONFile(path, out simple); //comment this out and it works as expected
FileOperations.WriteJSONFile(path, simple);
FileOperations.WriteJSONFile(path, simple);
FileOperations.WriteJSONFile(path, simple);
FileOperations.WriteJSONFile(path, simple);
}
}
public class Simple
{
public string Name { get; set; }
public DataTable dt { get; set; }
public Simple()
{
Name = "Test Name";
dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Num", typeof(long));
dt.Columns.Add("Test,typeof(string)");
dt.Rows.Add("Buckaroo Banzai", 1, "abcd");
dt.Rows.Add("Hoban Washburne", 1, "abcd");
dt.Rows.Add("Dr. Morbius", 1, "abcd");
}
}
I think the situation happens on this line:
FileOperations.ReadJSONFile(path, out simple);
This line instantiates a Simple object
This calls the constructor
The constructor fills the datatable with 3 rows
Then the ReadJSONFile method fills the object with data from the file - adding another 3 rows