diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs
index 7f195085d2..d3d5d4c83f 100644
--- a/Source/NETworkManager.Settings/SettingsInfo.cs
+++ b/Source/NETworkManager.Settings/SettingsInfo.cs
@@ -13,7 +13,7 @@
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
-using System.Xml.Serialization;
+using System.Text.Json.Serialization;
// ReSharper disable InconsistentNaming
@@ -42,7 +42,7 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null)
#region Variables
- [XmlIgnore] public bool SettingsChanged { get; set; }
+ [JsonIgnore] public bool SettingsChanged { get; set; }
///
/// Private field for the property.
diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs
index cbd20d5ff4..1ce2e093d4 100644
--- a/Source/NETworkManager.Settings/SettingsManager.cs
+++ b/Source/NETworkManager.Settings/SettingsManager.cs
@@ -1,9 +1,12 @@
using log4net;
using NETworkManager.Models;
using NETworkManager.Models.Network;
+using NETworkManager.Utilities;
using System;
using System.IO;
using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Xml.Serialization;
namespace NETworkManager.Settings;
@@ -22,6 +25,11 @@ public static class SettingsManager
///
private static string SettingsFolderName => "Settings";
+ ///
+ /// Settings backups directory name.
+ ///
+ private static string BackupFolderName => "Backups";
+
///
/// Settings file name.
///
@@ -30,7 +38,13 @@ public static class SettingsManager
///
/// Settings file extension.
///
- private static string SettingsFileExtension => ".xml";
+ private static string SettingsFileExtension => ".json";
+
+ ///
+ /// Legacy XML settings file extension.
+ ///
+ [Obsolete("Legacy XML settings are no longer used, but the extension is kept for migration purposes.")]
+ private static string LegacySettingsFileExtension => ".xml";
///
/// Settings that are currently loaded.
@@ -42,6 +56,17 @@ public static class SettingsManager
///
public static bool HotKeysChanged { get; set; }
+ ///
+ /// JSON serializer options for consistent serialization/deserialization.
+ ///
+ private static readonly JsonSerializerOptions JsonOptions = new()
+ {
+ WriteIndented = true,
+ PropertyNameCaseInsensitive = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.Never,
+ Converters = { new JsonStringEnumConverter() }
+ };
+
#endregion
#region Settings location, default paths and file names
@@ -58,6 +83,15 @@ public static string GetSettingsFolderLocation()
AssemblyManager.Current.Name, SettingsFolderName);
}
+ ///
+ /// Method to get the path of the settings backup folder.
+ ///
+ /// Path to the settings backup folder.
+ public static string GetSettingsBackupFolderLocation()
+ {
+ return Path.Combine(GetSettingsFolderLocation(), BackupFolderName);
+ }
+
///
/// Method to get the settings file name.
///
@@ -67,6 +101,16 @@ public static string GetSettingsFileName()
return $"{SettingsFileName}{SettingsFileExtension}";
}
+ ///
+ /// Method to get the legacy settings file name.
+ ///
+ /// Legacy settings file name.
+ [Obsolete("Legacy XML settings are no longer used, but the method is kept for migration purposes.")]
+ public static string GetLegacySettingsFileName()
+ {
+ return $"{SettingsFileName}{LegacySettingsFileExtension}";
+ }
+
///
/// Method to get the settings file path.
///
@@ -76,6 +120,16 @@ public static string GetSettingsFilePath()
return Path.Combine(GetSettingsFolderLocation(), GetSettingsFileName());
}
+ ///
+ /// Method to get the legacy XML settings file path.
+ ///
+ /// Legacy XML settings file path.
+ [Obsolete("Legacy XML settings are no longer used, but the method is kept for migration purposes.")]
+ private static string GetLegacySettingsFilePath()
+ {
+ return Path.Combine(GetSettingsFolderLocation(), GetLegacySettingsFileName());
+ }
+
#endregion
#region Initialize, load and save
@@ -99,7 +153,9 @@ public static void Initialize()
public static void Load()
{
var filePath = GetSettingsFilePath();
+ var legacyFilePath = GetLegacySettingsFilePath();
+ // Check if JSON file exists
if (File.Exists(filePath))
{
Current = DeserializeFromFile(filePath);
@@ -109,22 +165,66 @@ public static void Load()
return;
}
+ // Check if legacy XML file exists and migrate it
+ if (File.Exists(legacyFilePath))
+ {
+ Log.Info("Legacy XML settings file found. Migrating to JSON format...");
+
+ Current = DeserializeFromXmlFile(legacyFilePath);
+
+ Current.SettingsChanged = false;
+
+ // Save in new JSON format
+ Save();
+
+ // Create a backup of the legacy XML file and delete the original
+ Directory.CreateDirectory(GetSettingsBackupFolderLocation());
+
+ var backupFilePath = Path.Combine(GetSettingsBackupFolderLocation(),
+ $"{TimestampHelper.GetTimestamp()}_{GetLegacySettingsFileName()}");
+
+ File.Copy(legacyFilePath, backupFilePath, true);
+
+ File.Delete(legacyFilePath);
+
+ Log.Info($"Legacy XML settings file backed up to: {backupFilePath}");
+
+ Log.Info("Settings migration from XML to JSON completed successfully.");
+
+ return;
+ }
+
// Initialize the default settings if there is no settings file.
Initialize();
}
///
- /// Method to deserialize the settings from a file.
+ /// Method to deserialize the settings from a JSON file.
///
/// Path to the settings file.
/// Settings as .
private static SettingsInfo DeserializeFromFile(string filePath)
+ {
+ var jsonString = File.ReadAllText(filePath);
+
+ var settingsInfo = JsonSerializer.Deserialize(jsonString, JsonOptions);
+
+ return settingsInfo;
+ }
+
+ ///
+ /// Method to deserialize the settings from a legacy XML file.
+ ///
+ /// Path to the XML settings file.
+ /// Settings as .
+ [Obsolete("Legacy XML settings are no longer used, but the method is kept for migration purposes.")]
+ private static SettingsInfo DeserializeFromXmlFile(string filePath)
{
var xmlSerializer = new XmlSerializer(typeof(SettingsInfo));
using var fileStream = new FileStream(filePath, FileMode.Open);
- var settingsInfo = (SettingsInfo)xmlSerializer.Deserialize(fileStream);
+ var settingsInfo = xmlSerializer.Deserialize(fileStream) as SettingsInfo;
return settingsInfo;
}
@@ -145,17 +245,28 @@ public static void Save()
}
///
- /// Method to serialize the settings to a file.
+ /// Method to serialize the settings to a JSON file.
///
/// Path to the settings file.
private static void SerializeToFile(string filePath)
{
- var xmlSerializer = new XmlSerializer(typeof(SettingsInfo));
+ var jsonString = JsonSerializer.Serialize(Current, JsonOptions);
+
+ File.WriteAllText(filePath, jsonString);
+ }
- using var fileStream = new FileStream(filePath, FileMode.Create);
+ #endregion
+
+ #region Backup
+ /*
+ private static void Backup()
+ {
+ Log.Info("Creating settings backup...");
- xmlSerializer.Serialize(fileStream, Current);
+ // Create the backup directory if it does not exist
+ Directory.CreateDirectory(GetSettingsBackupFolderLocation());
}
+ */
#endregion
diff --git a/Source/NETworkManager/App.xaml.cs b/Source/NETworkManager/App.xaml.cs
index 249efe1a39..ace2a1267f 100644
--- a/Source/NETworkManager/App.xaml.cs
+++ b/Source/NETworkManager/App.xaml.cs
@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Text.Json;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
@@ -92,21 +93,15 @@ by BornToBeRoot
}
catch (InvalidOperationException ex)
{
- Log.Error("Could not load application settings!");
- Log.Error(ex.Message + "-" + ex.StackTrace);
-
- // Create backup of corrupted file
- var destinationFile =
- $"{TimestampHelper.GetTimestamp()}_corrupted_" + SettingsManager.GetSettingsFileName();
- File.Copy(SettingsManager.GetSettingsFilePath(),
- Path.Combine(SettingsManager.GetSettingsFolderLocation(), destinationFile));
- Log.Info($"A backup of the corrupted settings file has been saved under {destinationFile}");
-
- // Initialize default application settings
- Log.Info("Initialize default application settings...");
+ Log.Error("Could not load application settings!", ex);
+
+ HandleCorruptedSettingsFile();
+ }
+ catch (JsonException ex)
+ {
+ Log.Error("Could not load application settings! JSON file is corrupted or invalid.", ex);
- SettingsManager.Initialize();
- ConfigurationManager.Current.ShowSettingsResetNoteOnStartup = true;
+ HandleCorruptedSettingsFile();
}
// Upgrade settings if necessary
@@ -220,6 +215,27 @@ by BornToBeRoot
}
}
+ ///
+ /// Handles a corrupted settings file by creating a backup and initializing default settings.
+ ///
+ private void HandleCorruptedSettingsFile()
+ {
+ // Create backup of corrupted file
+ var destinationFile =
+ $"{TimestampHelper.GetTimestamp()}_corrupted_" + SettingsManager.GetSettingsFileName();
+
+ File.Copy(SettingsManager.GetSettingsFilePath(),
+ Path.Combine(SettingsManager.GetSettingsFolderLocation(), destinationFile));
+
+ Log.Info($"A backup of the corrupted settings file has been saved under {destinationFile}");
+
+ // Initialize default application settings
+ Log.Info("Initialize default application settings...");
+
+ SettingsManager.Initialize();
+ ConfigurationManager.Current.ShowSettingsResetNoteOnStartup = true;
+ }
+
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Log.Info("Run background job...");
@@ -267,4 +283,4 @@ private void Save()
ProfileManager.Save();
}
}
-}
\ No newline at end of file
+}
diff --git a/Website/docs/changelog/next-release.md b/Website/docs/changelog/next-release.md
index a06d9051ae..417fc79f9c 100644
--- a/Website/docs/changelog/next-release.md
+++ b/Website/docs/changelog/next-release.md
@@ -51,6 +51,10 @@ Release date: **xx.xx.2025**
- Profile file dialog migrated to a child window to improve usability. [#3227](https://github.com/BornToBeRoot/NETworkManager/pull/3227)
- Credential dialogs migrated to child windows to improve usability. [#3231](https://github.com/BornToBeRoot/NETworkManager/pull/3231)
+**Settings**
+
+- Settings format migrated from `XML` to `JSON`. The settings file will be automatically converted on first start after the update. [#3282](https://github.com/BornToBeRoot/NETworkManager/pull/3282)
+
**DNS Lookup**
- Allow hostname as server address in addition to IP address in the add/edit server dialog. [#3261](https://github.com/BornToBeRoot/NETworkManager/pull/3261)