using RobotNet10.CustomConfiguration.Models;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace RobotNet10.CustomConfiguration.Helpers;
///
/// Helper class cho parse và serialize JSON config files
/// Format JSON mới: Object với metadata và variables
/// Format: {"id": "guid", "configType": "...", "createdAt": "...", "updatedAt": "...", "description": "...", "variables": [...]}
///
public static class JsonConfigParser
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
WriteIndented = true
};
///
/// Parse JSON stream thành ConfigFile (format mới với metadata)
///
public static ConfigFile ParseConfigFile(Stream jsonStream)
{
using var reader = new StreamReader(jsonStream);
var json = reader.ReadToEnd();
return ParseConfigFile(json);
}
///
/// Parse JSON string thành ConfigFile (format mới với metadata)
///
public static ConfigFile ParseConfigFile(string json)
{
ArgumentException.ThrowIfNullOrWhiteSpace(json, nameof(json));
using var jsonDoc = JsonDocument.Parse(json);
var root = jsonDoc.RootElement;
if (root.ValueKind != JsonValueKind.Object)
{
throw new ArgumentException("JSON must be an object with metadata and variables");
}
// Parse metadata
var id = root.TryGetProperty("id", out var idProp) && idProp.ValueKind == JsonValueKind.String
? Guid.Parse(idProp.GetString() ?? throw new ArgumentException("Invalid id format"))
: throw new ArgumentException("id is required");
var configType = root.GetProperty("configType").GetString()
?? throw new ArgumentException("configType is required");
var createdAt = root.TryGetProperty("createdAt", out var createdAtProp) && createdAtProp.ValueKind == JsonValueKind.String
? DateTime.Parse(createdAtProp.GetString() ?? throw new ArgumentException("Invalid createdAt format"))
: DateTime.UtcNow;
var updatedAt = root.TryGetProperty("updatedAt", out var updatedAtProp) && updatedAtProp.ValueKind == JsonValueKind.String
? DateTime.Parse(updatedAtProp.GetString() ?? throw new ArgumentException("Invalid updatedAt format"))
: DateTime.UtcNow;
var description = root.TryGetProperty("description", out var descProp) && descProp.ValueKind == JsonValueKind.String
? descProp.GetString()
: null;
// Parse variables
if (!root.TryGetProperty("variables", out var variablesProp) || variablesProp.ValueKind != JsonValueKind.Array)
{
throw new ArgumentException("variables array is required");
}
var variables = ParseVariablesArray(variablesProp);
return new ConfigFile
{
Id = id,
ConfigType = configType,
Variables = variables,
CreatedAt = createdAt,
UpdatedAt = updatedAt,
Description = description,
FilePath = $"configs/{configType}.config.json"
};
}
///
/// Parse variables array từ JSON element
///
private static List ParseVariablesArray(JsonElement variablesElement)
{
var variables = new List();
foreach (var element in variablesElement.EnumerateArray())
{
var variable = new ConfigVariable
{
Name = element.GetProperty("name").GetString() ?? throw new ArgumentException("Variable name is required"),
Type = VariableTypeConverter.ParseType(element.GetProperty("type").GetString() ?? throw new ArgumentException("Variable type is required")),
Value = ParseValue(element),
Min = element.TryGetProperty("min", out var minProp) && minProp.ValueKind != JsonValueKind.Null
? minProp.GetDouble()
: null,
Max = element.TryGetProperty("max", out var maxProp) && maxProp.ValueKind != JsonValueKind.Null
? maxProp.GetDouble()
: null,
Roles = element.TryGetProperty("roles", out var rolesProp)
? rolesProp.GetString() ?? string.Empty
: string.Empty,
EnumValues = element.TryGetProperty("enumValues", out var enumProp) && enumProp.ValueKind == JsonValueKind.Array
? [.. enumProp.EnumerateArray().Select(e => e.GetString() ?? string.Empty)]
: null
};
variables.Add(variable);
}
return variables;
}
///
/// Parse JSON stream thành list of ConfigVariable (backward compatibility cho import từ format cũ)
/// Format cũ: [{"name": "port", "type": "int", "value": 8080, "Min": 0, "Max": 65535, "Roles": ""}, ...]
///
public static List ParseVariables(Stream jsonStream)
{
using var reader = new StreamReader(jsonStream);
var json = reader.ReadToEnd();
return ParseVariables(json);
}
///
/// Parse JSON string thành list of ConfigVariable (backward compatibility cho import từ format cũ)
///
public static List ParseVariables(string json)
{
using var jsonDoc = JsonDocument.Parse(json);
var root = jsonDoc.RootElement;
// Check if it's new format (object) or old format (array)
if (root.ValueKind == JsonValueKind.Object)
{
// New format - extract variables
if (root.TryGetProperty("variables", out var variablesProp) && variablesProp.ValueKind == JsonValueKind.Array)
{
return ParseVariablesArray(variablesProp);
}
throw new ArgumentException("JSON object must contain 'variables' array");
}
if (root.ValueKind != JsonValueKind.Array)
{
throw new ArgumentException("JSON must be an array of variables (old format) or object with metadata and variables (new format)");
}
// Old format - array of variables
return ParseVariablesArray(root);
}
///
/// Parse value từ JSON element theo type
///
private static object? ParseValue(JsonElement element)
{
if (!element.TryGetProperty("value", out var valueProp))
{
throw new ArgumentException("Variable value is required");
}
var typeStr = element.GetProperty("type").GetString()?.ToLower();
return typeStr switch
{
"string" => valueProp.GetString(),
"int" => valueProp.GetInt32(),
"double" => valueProp.GetDouble(),
"bool" => valueProp.GetBoolean(),
"object" => ParseJsonObject(valueProp),
"array" => ParseJsonArray(valueProp),
"enum" => valueProp.GetString(), // Enum values are strings
_ => throw new ArgumentException($"Unsupported type: {typeStr}")
};
}
///
/// Parse JSON object thành Dictionary
///
private static Dictionary ParseJsonObject(JsonElement element)
{
if (element.ValueKind != JsonValueKind.Object)
{
throw new ArgumentException("Value must be a JSON object");
}
return JsonSerializer.Deserialize>( element.GetRawText()) ?? [];
}
///
/// Parse JSON array thành List
private static List