282 lines
11 KiB
C#
282 lines
11 KiB
C#
using RobotNet10.CustomConfiguration.Models;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace RobotNet10.CustomConfiguration.Helpers;
|
|
|
|
/// <summary>
|
|
/// 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": [...]}
|
|
/// </summary>
|
|
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
|
|
};
|
|
|
|
/// <summary>
|
|
/// Parse JSON stream thành ConfigFile (format mới với metadata)
|
|
/// </summary>
|
|
public static ConfigFile ParseConfigFile(Stream jsonStream)
|
|
{
|
|
using var reader = new StreamReader(jsonStream);
|
|
var json = reader.ReadToEnd();
|
|
return ParseConfigFile(json);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse JSON string thành ConfigFile (format mới với metadata)
|
|
/// </summary>
|
|
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"
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse variables array từ JSON element
|
|
/// </summary>
|
|
private static List<ConfigVariable> ParseVariablesArray(JsonElement variablesElement)
|
|
{
|
|
var variables = new List<ConfigVariable>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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": ""}, ...]
|
|
/// </summary>
|
|
public static List<ConfigVariable> ParseVariables(Stream jsonStream)
|
|
{
|
|
using var reader = new StreamReader(jsonStream);
|
|
var json = reader.ReadToEnd();
|
|
return ParseVariables(json);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse JSON string thành list of ConfigVariable (backward compatibility cho import từ format cũ)
|
|
/// </summary>
|
|
public static List<ConfigVariable> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse value từ JSON element theo type
|
|
/// </summary>
|
|
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}")
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse JSON object thành Dictionary<string, object>
|
|
/// </summary>
|
|
private static Dictionary<string, object> ParseJsonObject(JsonElement element)
|
|
{
|
|
if (element.ValueKind != JsonValueKind.Object)
|
|
{
|
|
throw new ArgumentException("Value must be a JSON object");
|
|
}
|
|
|
|
return JsonSerializer.Deserialize<Dictionary<string, object>>( element.GetRawText()) ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse JSON array thành List<object>
|
|
/// </summary>
|
|
private static List<object> ParseJsonArray(JsonElement element)
|
|
{
|
|
if (element.ValueKind != JsonValueKind.Array)
|
|
{
|
|
throw new ArgumentException("Value must be a JSON array");
|
|
}
|
|
|
|
var result = new List<object>();
|
|
foreach (var item in element.EnumerateArray())
|
|
{
|
|
result.Add(item.GetRawText());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize ConfigFile thành JSON string (format mới với metadata và variables)
|
|
/// </summary>
|
|
public static string SerializeConfigFile(ConfigFile config)
|
|
{
|
|
var jsonObject = new
|
|
{
|
|
id = config.Id,
|
|
configType = config.ConfigType,
|
|
createdAt = config.CreatedAt.ToString("O"), // ISO 8601 format
|
|
updatedAt = config.UpdatedAt.ToString("O"), // ISO 8601 format
|
|
description = config.Description,
|
|
variables = config.Variables.Select(v => new
|
|
{
|
|
name = v.Name,
|
|
type = v.Type.ToString().ToLower(),
|
|
value = SerializeValue(v.Value, v.Type),
|
|
min = v.Min,
|
|
max = v.Max,
|
|
roles = v.Roles ?? string.Empty,
|
|
enumValues = v.EnumValues
|
|
}).ToArray()
|
|
};
|
|
|
|
return JsonSerializer.Serialize(jsonObject, JsonOptions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize list of ConfigVariable thành JSON string (chỉ variables, dùng cho export backward compatibility)
|
|
/// </summary>
|
|
public static string SerializeVariables(List<ConfigVariable> variables)
|
|
{
|
|
var jsonArray = variables.Select(v => new
|
|
{
|
|
name = v.Name,
|
|
type = v.Type.ToString().ToLower(),
|
|
value = SerializeValue(v.Value, v.Type),
|
|
min = v.Min,
|
|
max = v.Max,
|
|
roles = v.Roles ?? string.Empty,
|
|
enumValues = v.EnumValues
|
|
}).ToArray();
|
|
|
|
return JsonSerializer.Serialize(jsonArray, JsonOptions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize value theo type (đặc biệt cho Object và Array)
|
|
/// </summary>
|
|
private static object SerializeValue(object? value, ConfigVariableType type)
|
|
{
|
|
if (value == null) return null!;
|
|
|
|
return type switch
|
|
{
|
|
ConfigVariableType.Object => value is Dictionary<string, object> dict
|
|
? dict
|
|
: System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(value.ToString() ?? "{}", JsonOptions) ?? [],
|
|
ConfigVariableType.Array => value is List<object> list
|
|
? list
|
|
: System.Text.Json.JsonSerializer.Deserialize<List<object>>(value.ToString() ?? "[]", JsonOptions) ?? [],
|
|
_ => value
|
|
};
|
|
}
|
|
}
|
|
|