Initial commit
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
using System.Globalization;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class cho convert giữa variable type và value
|
||||
/// </summary>
|
||||
public static class VariableTypeConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert string type name thành ConfigVariableType enum
|
||||
/// </summary>
|
||||
public static ConfigVariableType ParseType(string typeName)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(typeName, nameof(typeName));
|
||||
|
||||
return typeName.ToLower() switch
|
||||
{
|
||||
"string" => ConfigVariableType.String,
|
||||
"int" => ConfigVariableType.Int,
|
||||
"double" => ConfigVariableType.Double,
|
||||
"bool" => ConfigVariableType.Bool,
|
||||
"object" => ConfigVariableType.Object,
|
||||
"array" => ConfigVariableType.Array,
|
||||
"enum" => ConfigVariableType.Enum,
|
||||
_ => throw new ArgumentException($"Invalid variable type: {typeName}")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert value sang đúng type
|
||||
/// </summary>
|
||||
public static object ConvertValue(ConfigVariableType type, object? value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value), "Value cannot be null");
|
||||
|
||||
return type switch
|
||||
{
|
||||
ConfigVariableType.String => value.ToString() ?? string.Empty,
|
||||
ConfigVariableType.Int => Convert.ToInt32(value, CultureInfo.InvariantCulture),
|
||||
ConfigVariableType.Double => Convert.ToDouble(value, CultureInfo.InvariantCulture),
|
||||
ConfigVariableType.Bool => Convert.ToBoolean(value, CultureInfo.InvariantCulture),
|
||||
ConfigVariableType.Object => ConvertToObject(value),
|
||||
ConfigVariableType.Array => ConvertToArray(value),
|
||||
ConfigVariableType.Enum => value.ToString() ?? string.Empty, // Enum values are strings
|
||||
_ => throw new ArgumentException($"Unsupported type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert value thành Dictionary<string, object> (Object type)
|
||||
/// </summary>
|
||||
private static Dictionary<string, object> ConvertToObject(object value)
|
||||
{
|
||||
if (value is Dictionary<string, object> dict)
|
||||
return dict;
|
||||
|
||||
if (value is System.Text.Json.JsonElement jsonElement && jsonElement.ValueKind == System.Text.Json.JsonValueKind.Object)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var prop in jsonElement.EnumerateObject())
|
||||
{
|
||||
result[prop.Name] = prop.Value.GetRawText();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Try parse as JSON string
|
||||
var jsonString = value.ToString();
|
||||
if (!string.IsNullOrEmpty(jsonString))
|
||||
{
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(jsonString);
|
||||
if (doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Object)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var prop in doc.RootElement.EnumerateObject())
|
||||
{
|
||||
result[prop.Name] = prop.Value.GetRawText();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException("Value cannot be converted to Object type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert value thành List<object> (Array type)
|
||||
/// </summary>
|
||||
private static List<object> ConvertToArray(object value)
|
||||
{
|
||||
if (value is List<object> list)
|
||||
return list;
|
||||
|
||||
if (value is System.Text.Json.JsonElement jsonElement && jsonElement.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||
{
|
||||
var result = new List<object>();
|
||||
foreach (var item in jsonElement.EnumerateArray())
|
||||
{
|
||||
result.Add(item.GetRawText());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Try parse as JSON string
|
||||
var jsonString = value.ToString();
|
||||
if (!string.IsNullOrEmpty(jsonString))
|
||||
{
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(jsonString);
|
||||
if (doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||
{
|
||||
var result = new List<object>();
|
||||
foreach (var item in doc.RootElement.EnumerateArray())
|
||||
{
|
||||
result.Add(item.GetRawText());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException("Value cannot be converted to Array type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate value có đúng type không
|
||||
/// </summary>
|
||||
public static bool IsValidValue(ConfigVariableType type, object? value)
|
||||
{
|
||||
if (value == null) return false;
|
||||
|
||||
return type switch
|
||||
{
|
||||
ConfigVariableType.String => value is string,
|
||||
ConfigVariableType.Int => value is int || int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out _),
|
||||
ConfigVariableType.Double => value is double || double.TryParse(value.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out _),
|
||||
ConfigVariableType.Bool => value is bool || bool.TryParse(value.ToString(), out _),
|
||||
ConfigVariableType.Object => value is Dictionary<string, object> ||
|
||||
(value is System.Text.Json.JsonElement je && je.ValueKind == System.Text.Json.JsonValueKind.Object) ||
|
||||
TryParseJsonObject(value),
|
||||
ConfigVariableType.Array => value is List<object> ||
|
||||
(value is System.Text.Json.JsonElement je && je.ValueKind == System.Text.Json.JsonValueKind.Array) ||
|
||||
TryParseJsonArray(value),
|
||||
ConfigVariableType.Enum => value is string, // Enum values are always strings
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private static bool TryParseJsonObject(object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonString = value.ToString();
|
||||
if (string.IsNullOrEmpty(jsonString)) return false;
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(jsonString);
|
||||
return doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Object;
|
||||
}
|
||||
catch (System.Text.Json.JsonException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseJsonArray(object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonString = value.ToString();
|
||||
if (string.IsNullOrEmpty(jsonString)) return false;
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(jsonString);
|
||||
return doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Array;
|
||||
}
|
||||
catch (System.Text.Json.JsonException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user