Сериализация сущностей для отображения в веб-интерфейсе

This commit is contained in:
2025-10-06 15:43:38 +04:00
parent 7e32280093
commit 1300f31234
4 changed files with 298 additions and 73 deletions
+247
View File
@@ -0,0 +1,247 @@
using System.Collections;
using System.Reflection;
using Pf2e.Common;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Common.I18n;
using Pf2e.Database.Model.Enum;
namespace Pf2e.Web.Helpers;
/// <summary>
/// Сериализатор сущности для отображения на веб-странице.
/// </summary>
public class EntitySerializer
{
/// <summary>
/// Результат сериализации сущности.
/// </summary>
public class SerializationResult
{
public List<FieldGroup> Groups { get; set; } = [];
}
/// <summary>
/// Группа полей.
/// </summary>
public class FieldGroup
{
public string Name { get; set; } = string.Empty;
public List<FieldInfo> Fields { get; set; } = [];
}
/// <summary>
/// Информация о поле.
/// </summary>
public class FieldInfo
{
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public object? Value { get; set; }
public string DisplayValue { get; set; } = string.Empty;
public bool IsImplemented { get; set; } = true;
}
/// <summary>
/// Сериализовать FullEntity.
/// </summary>
public SerializationResult Serialize(FullEntity fullEntity)
{
SerializationResult result = new();
// Группа для основных полей Entity
FieldGroup entityGroup = new() { Name = "Основные поля" };
AddFieldsToGroup(entityGroup, fullEntity.Entity);
result.Groups.Add(entityGroup);
// Группа для полей IEntity (SubEntity)
if (fullEntity.SubEntity != null)
{
FieldGroup subEntityGroup = new()
{
Name = "Поля " + fullEntity.SubEntity.GetType().Name,
};
AddFieldsToGroup(subEntityGroup, fullEntity.SubEntity);
result.Groups.Add(subEntityGroup);
}
// Группа для ссылок
FieldGroup referencesGroup = new() { Name = "Ссылки" };
AddReferencesToGroup(referencesGroup, fullEntity);
result.Groups.Add(referencesGroup);
return result;
}
/// <summary>
/// Добавить поля объекта в группу.
/// </summary>
private void AddFieldsToGroup(FieldGroup group, object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
// Пропускаем навигационные свойства Entity
if (property.Name == "Entity")
continue;
FieldInfo fieldInfo = new()
{
Name = property.Name,
Type = GetTypeDisplayName(property.PropertyType),
Value = property.GetValue(obj),
};
// Обработка значения для отображения
var (displayValue, isImplemented) = FormatValue(fieldInfo.Value);
fieldInfo.DisplayValue = displayValue;
fieldInfo.IsImplemented = isImplemented;
group.Fields.Add(fieldInfo);
}
}
/// <summary>
/// Добавить ссылки в группу.
/// </summary>
private void AddReferencesToGroup(FieldGroup group, FullEntity fullEntity)
{
// Добавляем информацию о связанных сущностях
Entity entity = fullEntity.Entity;
// Traits
if (entity.Traits?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Traits);
group.Fields.Add(
new FieldInfo
{
Name = "Traits",
Type = "ICollection<Trait>",
Value = entity.Traits,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
// Keywords
if (entity.Keywords?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Keywords);
group.Fields.Add(
new FieldInfo
{
Name = "Keywords",
Type = "ICollection<Keyword>",
Value = entity.Keywords,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
// Sources
if (entity.Sources?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Sources);
group.Fields.Add(
new FieldInfo
{
Name = "Sources",
Type = "ICollection<EntitySource>",
Value = entity.Sources,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
// Externals
if (entity.Externals?.Any() == true)
{
var (displayValue, isImplemented) = FormatValue(entity.Externals);
group.Fields.Add(
new()
{
Name = "Externals",
Type = "ICollection<EntityExternal>",
Value = entity.Externals,
DisplayValue = displayValue,
IsImplemented = isImplemented,
}
);
}
}
/// <summary>
/// Получить отображаемое имя типа.
/// </summary>
private string GetTypeDisplayName(Type type)
{
if (type.IsEnum)
return "enum";
return type.Name;
}
/// <summary>
/// Форматировать значение для отображения.
/// </summary>
private (string displayValue, bool isImplemented) FormatValue(object? value)
{
if (value == null)
return ("null", true);
Type type = value.GetType();
// Обработка перечислений
if (type.IsEnum)
{
Enum enumValue = (Enum)value;
return ($"{enumValue} ({(int)(object)enumValue})", true);
}
// Обработка примитивных типов
if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal))
{
return (value.ToString() ?? "null", true);
}
// Обработка IEntity
if (typeof(IEntity).IsAssignableFrom(type))
{
var entity = (IEntity)value;
var entityType = entity.Entity.Type;
var slug = entity.Entity.Slug;
var name = entity.Entity.Name[LocaleType.English];
return ($"<a href=\"/Entity/{entityType}/{slug}\">{name}</a>", true);
}
// Обработка коллекций
if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string))
{
var collection = (IEnumerable)value;
var items = new List<string>();
bool allImplemented = true;
foreach (var item in collection)
{
var (itemDisplay, itemImplemented) = FormatValue(item);
items.Add(itemDisplay);
if (!itemImplemented)
allImplemented = false;
}
if (items.Count == 0)
return ("(empty)", true);
return (string.Join(", ", items), allImplemented);
}
// Для остальных сложных объектов
return ($"[{type.Name}] - НЕ РЕАЛИЗОВАНО", false);
}
}