using System; using System.IO; using System.Runtime.InteropServices.ComTypes; using System.Text.Json; using System.Text.Json.Nodes; using Config.Net.Core; namespace Config.Net.Stores { /// /// Simple JSON storage using System.Text.Json /// public class JsonConfigStore : IConfigStore { private readonly string? _pathName; private JsonNode? _j; /// /// Create JSON storage in the file specified in . /// /// Name of the file, either path to JSON storage file, or json file content. /// Set to true if specifies file name, otherwise false. /// is null. /// Provided path is not valid. /// Storage file does not have to exist, however it will be created as soon as first write performed. public JsonConfigStore(string name, bool isFilePath) { if (name == null) throw new ArgumentNullException(nameof(name)); if (isFilePath) { _pathName = Path.GetFullPath(name); // Allow relative path to JSON file _j = ReadJsonFile(_pathName); } else { _j = ReadJsonString(name); } } public void Dispose() { // nothing to dispose. } public static string Name => "json"; public bool CanRead => true; public bool CanWrite => _pathName != null; public string? Read(string rawKey) { if (string.IsNullOrEmpty(rawKey) || _j == null) return null; bool isLength = OptionPath.TryStripLength(rawKey, out string? key); if (key == null) return null; string[] parts = key.Split('.'); if (parts.Length == 0) return null; JsonNode? node = _j; foreach (string rawPart in parts) { bool isIndex = OptionPath.TryStripIndex(rawPart, out string? part, out int partIndex); if (part == null) return null; node = node![part]; if (node == null) return null; if (isIndex) { if (node is not JsonArray ja) return null; if (partIndex < ja.Count) { node = ja[partIndex]; } else return null; } } if (isLength) return node is JsonArray ja ? ja.Count.ToString() : null; return node!.ToString(); } public void Write(string key, string? value) { if (string.IsNullOrEmpty(_pathName)) throw new InvalidOperationException("please specify file name for writeable config"); if (_j == null) _j = new JsonObject(); // navigate to target element, create if needed string[] parts = key.Split('.'); if (parts.Length == 0) return; JsonNode? node = _j; string? lastPart = null; foreach (string rawPart in parts) { bool isIndex = OptionPath.TryStripIndex(rawPart, out string? part, out int partIndex); if (part == null) return; lastPart = part; JsonNode? nextNode = node[part]; if (isIndex) { throw new NotImplementedException(); } else { if (nextNode == null) { //create missing node nextNode = new JsonObject(); node[part] = nextNode; } } node = nextNode; } JsonObject? parent = node.Parent as JsonObject; parent!.Remove(lastPart!); parent![lastPart!] = JsonValue.Create(value); string js = _j.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); FileInfo file = new(_pathName); if (file is not null) { if (file.Directory is not null) { file.Directory.Create(); } } File.WriteAllText(_pathName, js); } private static JsonNode? ReadJsonFile(string fileName) { if (File.Exists(fileName)) { string json = File.ReadAllText(fileName); return ReadJsonString(json); } return null; } private static JsonNode? ReadJsonString(string jsonString) { return JsonNode.Parse(jsonString); } } }