// // Copyright (c) PlaceholderCompany. All rights reserved. // namespace Emby.Server.Implementations.AppBase { using System; using System.IO; using System.Text.Json; using Jellyfin.Extensions.Json; using MediaBrowser.Model.Serialization; /// /// Class ConfigurationHelper. /// public static class ConfigurationHelper { /// /// Reads an xml configuration file from the file system /// It will immediately re-serialize and save if new serialization data is available due to property changes. /// /// The type. /// The path. /// The XML serializer. /// System.Object. public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer) { object configuration; byte[]? buffer = null; // Use try/catch to avoid the extra file system lookup using File.Exists try { buffer = File.ReadAllBytes(path); configuration = xmlSerializer.DeserializeFromBytes(type, buffer); } catch (Exception) { // Note: CreateInstance returns null for Nullable, e.g. CreateInstance(typeof(int?)) returns null. configuration = Activator.CreateInstance(type)!; } using var stream = new MemoryStream(buffer?.Length ?? 0); xmlSerializer.SerializeToStream(configuration, stream); // Take the object we just got and serialize it back to bytes Span newBytes = stream.GetBuffer().AsSpan(0, (int)stream.Length); // If the file didn't exist before, or if something has changed, re-save if (buffer is null || !newBytes.SequenceEqual(buffer)) { var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"Provided path ({path}) is not valid.", nameof(path)); Directory.CreateDirectory(directory); // Save it after load in case we got new items #pragma warning disable IDE0063 // Use simple 'using' statement using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) { fs.Write(newBytes); } #pragma warning restore IDE0063 // Use simple 'using' statement } return configuration; } /// /// Reads a JSON configuration file from the file system using System.Text.Json. /// It will immediately re-serialize and save if new serialization data is available due to property changes. /// /// The type of configuration to deserialize. /// The path to the JSON configuration file. /// The deserialized configuration object. public static T GetJsonConfiguration(string path) where T : class, new() { T configuration; byte[]? buffer = null; // Use try/catch to avoid the extra file system lookup using File.Exists try { buffer = File.ReadAllBytes(path); configuration = JsonSerializer.Deserialize(buffer, JsonDefaults.Options) ?? new T(); } catch (Exception) { // If file doesn't exist or deserialization fails, create a new instance with defaults configuration = new T(); } // Re-serialize to ensure proper formatting and any new defaults are captured byte[] newBytes = JsonSerializer.SerializeToUtf8Bytes(configuration, JsonDefaults.Options); // If the file didn't exist before, or if something has changed, re-save if (buffer is null || !newBytes.AsSpan().SequenceEqual(buffer)) { var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"Provided path ({path}) is not valid.", nameof(path)); Directory.CreateDirectory(directory); // Save it after load in case we got new items or formatting changed File.WriteAllBytes(path, newBytes); } return configuration; } } }