refactor(render): 抽象图形后端并迁移渲染器 (#313)
* refactor(render): 抽象图形后端并迁移渲染器 - 新增 engine-shared 包,定义 GraphicsBackend trait 抽象层 - 实现 WebGL2Backend 作为首个后端实现 - 迁移 Renderer2D、SpriteBatch、GridRenderer、GizmoRenderer 使用新抽象 - 修复 VAO 创建时索引缓冲区绑定状态泄漏问题 - 新增 create_vertex_buffer_sized 方法支持预分配缓冲区 * fix(serialization): 修复序列化循环引用导致栈溢出 - 在 serializeValue 添加 WeakSet 检测循环引用 - 跳过已访问对象避免无限递归 * refactor(serialization): 提取 ValueSerializer 统一序列化逻辑 - 新增 ValueSerializer 模块,函数式设计 - 支持可扩展类型处理器注册 - 移除 ComponentSerializer/SceneSerializer 重复代码 - 内置 Date/Map/Set 类型支持 * fix: CodeQL 类型检查警告
This commit is contained in:
@@ -1,363 +1,130 @@
|
||||
/**
|
||||
* 组件序列化器
|
||||
*
|
||||
* 负责组件的序列化和反序列化操作
|
||||
* Component serializer for ECS components.
|
||||
*/
|
||||
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { getComponentTypeName, isEntityRefProperty } from '../Decorators';
|
||||
import {
|
||||
getSerializationMetadata
|
||||
} from './SerializationDecorators';
|
||||
import { getSerializationMetadata } from './SerializationDecorators';
|
||||
import { ValueSerializer, SerializableValue } from './ValueSerializer';
|
||||
import type { Entity } from '../Entity';
|
||||
import type { SerializationContext, SerializedEntityRef } from './SerializationContext';
|
||||
|
||||
/**
|
||||
* 可序列化的值类型
|
||||
*/
|
||||
export type SerializableValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| SerializableValue[]
|
||||
| { [key: string]: SerializableValue }
|
||||
| { __type: 'Date'; value: string }
|
||||
| { __type: 'Map'; value: Array<[SerializableValue, SerializableValue]> }
|
||||
| { __type: 'Set'; value: SerializableValue[] }
|
||||
| { __entityRef: SerializedEntityRef };
|
||||
export type { SerializableValue } from './ValueSerializer';
|
||||
|
||||
/**
|
||||
* 序列化后的组件数据
|
||||
*/
|
||||
export interface SerializedComponent {
|
||||
/**
|
||||
* 组件类型名称
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* 序列化版本
|
||||
*/
|
||||
version: number;
|
||||
|
||||
/**
|
||||
* 组件数据
|
||||
*/
|
||||
data: Record<string, SerializableValue>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件序列化器类
|
||||
*/
|
||||
export class ComponentSerializer {
|
||||
/**
|
||||
* 序列化单个组件
|
||||
*
|
||||
* @param component 要序列化的组件实例
|
||||
* @returns 序列化后的组件数据,如果组件不可序列化则返回null
|
||||
*/
|
||||
public static serialize(component: Component): SerializedComponent | null {
|
||||
static serialize(component: Component): SerializedComponent | null {
|
||||
const metadata = getSerializationMetadata(component);
|
||||
|
||||
if (!metadata) {
|
||||
// 组件没有使用@Serializable装饰器,不可序列化
|
||||
return null;
|
||||
}
|
||||
if (!metadata) return null;
|
||||
|
||||
const componentType = component.constructor as ComponentType;
|
||||
const typeName = metadata.options.typeId || getComponentTypeName(componentType);
|
||||
const data: Record<string, SerializableValue> = {};
|
||||
|
||||
// 序列化标记的字段
|
||||
for (const [fieldName, options] of metadata.fields) {
|
||||
if (metadata.ignoredFields.has(fieldName)) continue;
|
||||
|
||||
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
|
||||
const value = (component as unknown as Record<string | symbol, unknown>)[fieldName];
|
||||
|
||||
// 跳过忽略的字段
|
||||
if (metadata.ignoredFields.has(fieldName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let serializedValue: SerializableValue;
|
||||
|
||||
// 检查是否为 EntityRef 属性
|
||||
if (isEntityRefProperty(component, fieldKey)) {
|
||||
serializedValue = this.serializeEntityRef(value as Entity | null);
|
||||
} else if (options.serializer) {
|
||||
// 使用自定义序列化器
|
||||
serializedValue = options.serializer(value);
|
||||
} else {
|
||||
// 使用默认序列化
|
||||
serializedValue = this.serializeValue(value as SerializableValue);
|
||||
serializedValue = ValueSerializer.serialize(value);
|
||||
}
|
||||
|
||||
// 使用别名或原始字段名
|
||||
const key = options.alias || fieldKey;
|
||||
data[key] = serializedValue;
|
||||
data[options.alias || fieldKey] = serializedValue;
|
||||
}
|
||||
|
||||
return {
|
||||
type: typeName,
|
||||
version: metadata.options.version,
|
||||
data
|
||||
};
|
||||
return { type: typeName, version: metadata.options.version, data };
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化组件
|
||||
*
|
||||
* @param serializedData 序列化的组件数据
|
||||
* @param componentRegistry 组件类型注册表 (类型名 -> 构造函数)
|
||||
* @param context 序列化上下文(可选,用于解析 EntityRef)
|
||||
* @returns 反序列化后的组件实例,如果失败则返回null
|
||||
*/
|
||||
public static deserialize(
|
||||
static deserialize(
|
||||
serializedData: SerializedComponent,
|
||||
componentRegistry: Map<string, ComponentType>,
|
||||
context?: SerializationContext
|
||||
): Component | null {
|
||||
const componentClass = componentRegistry.get(serializedData.type);
|
||||
|
||||
if (!componentClass) {
|
||||
console.warn(`未找到组件类型: ${serializedData.type}`);
|
||||
console.warn(`Component type not found: ${serializedData.type}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadata = getSerializationMetadata(componentClass);
|
||||
|
||||
if (!metadata) {
|
||||
console.warn(`组件 ${serializedData.type} 不可序列化`);
|
||||
console.warn(`Component ${serializedData.type} is not serializable`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 创建组件实例
|
||||
const component = new componentClass();
|
||||
|
||||
// 反序列化字段
|
||||
for (const [fieldName, options] of metadata.fields) {
|
||||
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
|
||||
const key = options.alias || fieldKey;
|
||||
const serializedValue = serializedData.data[key];
|
||||
|
||||
if (serializedValue === undefined) {
|
||||
continue; // 字段不存在于序列化数据中
|
||||
}
|
||||
if (serializedValue === undefined) continue;
|
||||
|
||||
// 检查是否为序列化的 EntityRef
|
||||
if (this.isSerializedEntityRef(serializedValue)) {
|
||||
// EntityRef 需要延迟解析
|
||||
if (context) {
|
||||
const ref = serializedValue.__entityRef;
|
||||
context.registerPendingRef(component, fieldKey, ref.id, ref.guid);
|
||||
}
|
||||
// 暂时设为 null,后续由 context.resolveAllReferences() 填充
|
||||
(component as unknown as Record<string | symbol, unknown>)[fieldName] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 使用自定义反序列化器或默认反序列化
|
||||
const value = options.deserializer
|
||||
? options.deserializer(serializedValue)
|
||||
: this.deserializeValue(serializedValue);
|
||||
: ValueSerializer.deserialize(serializedValue);
|
||||
|
||||
(component as unknown as Record<string | symbol, SerializableValue>)[fieldName] = value;
|
||||
(component as unknown as Record<string | symbol, unknown>)[fieldName] = value;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量序列化组件
|
||||
*
|
||||
* @param components 组件数组
|
||||
* @returns 序列化后的组件数据数组
|
||||
*/
|
||||
public static serializeComponents(components: Component[]): SerializedComponent[] {
|
||||
const result: SerializedComponent[] = [];
|
||||
|
||||
for (const component of components) {
|
||||
const serialized = this.serialize(component);
|
||||
if (serialized) {
|
||||
result.push(serialized);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
static serializeComponents(components: Component[]): SerializedComponent[] {
|
||||
return components
|
||||
.map(c => this.serialize(c))
|
||||
.filter((s): s is SerializedComponent => s !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量反序列化组件
|
||||
*
|
||||
* @param serializedComponents 序列化的组件数据数组
|
||||
* @param componentRegistry 组件类型注册表
|
||||
* @param context 序列化上下文(可选,用于解析 EntityRef)
|
||||
* @returns 反序列化后的组件数组
|
||||
*/
|
||||
public static deserializeComponents(
|
||||
static deserializeComponents(
|
||||
serializedComponents: SerializedComponent[],
|
||||
componentRegistry: Map<string, ComponentType>,
|
||||
context?: SerializationContext
|
||||
): Component[] {
|
||||
const result: Component[] = [];
|
||||
|
||||
for (const serialized of serializedComponents) {
|
||||
const component = this.deserialize(serialized, componentRegistry, context);
|
||||
if (component) {
|
||||
result.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return serializedComponents
|
||||
.map(s => this.deserialize(s, componentRegistry, context))
|
||||
.filter((c): c is Component => c !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认值序列化
|
||||
*
|
||||
* 处理基本类型、数组、对象等的序列化
|
||||
*/
|
||||
private static serializeValue(value: SerializableValue): SerializableValue {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 日期
|
||||
if (value instanceof Date) {
|
||||
return {
|
||||
__type: 'Date',
|
||||
value: value.toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => this.serializeValue(item));
|
||||
}
|
||||
|
||||
// Map (如果没有使用@SerializeMap装饰器)
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
__type: 'Map',
|
||||
value: Array.from(value.entries())
|
||||
};
|
||||
}
|
||||
|
||||
// Set
|
||||
if (value instanceof Set) {
|
||||
return {
|
||||
__type: 'Set',
|
||||
value: Array.from(value)
|
||||
};
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object' && typeof value === 'object' && !Array.isArray(value)) {
|
||||
const result: Record<string, SerializableValue> = {};
|
||||
const obj = value as Record<string, SerializableValue>;
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
result[key] = this.serializeValue(obj[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 其他类型(函数等)不序列化
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认值反序列化
|
||||
*/
|
||||
private static deserializeValue(value: SerializableValue): SerializableValue {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型直接返回
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 处理特殊类型标记
|
||||
if (type === 'object' && typeof value === 'object' && '__type' in value) {
|
||||
const typedValue = value as { __type: string; value: SerializableValue };
|
||||
switch (typedValue.__type) {
|
||||
case 'Date':
|
||||
return { __type: 'Date', value: typeof typedValue.value === 'string' ? typedValue.value : String(typedValue.value) };
|
||||
case 'Map':
|
||||
return { __type: 'Map', value: typedValue.value as Array<[SerializableValue, SerializableValue]> };
|
||||
case 'Set':
|
||||
return { __type: 'Set', value: typedValue.value as SerializableValue[] };
|
||||
}
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => this.deserializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object' && typeof value === 'object' && !Array.isArray(value)) {
|
||||
const result: Record<string, SerializableValue> = {};
|
||||
const obj = value as Record<string, SerializableValue>;
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
result[key] = this.deserializeValue(obj[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证序列化数据的版本
|
||||
*
|
||||
* @param serializedData 序列化数据
|
||||
* @param expectedVersion 期望的版本号
|
||||
* @returns 版本是否匹配
|
||||
*/
|
||||
public static validateVersion(
|
||||
serializedData: SerializedComponent,
|
||||
expectedVersion: number
|
||||
): boolean {
|
||||
static validateVersion(serializedData: SerializedComponent, expectedVersion: number): boolean {
|
||||
return serializedData.version === expectedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的序列化信息
|
||||
*
|
||||
* @param component 组件实例或组件类
|
||||
* @returns 序列化信息对象,包含类型名、版本、可序列化字段列表
|
||||
*/
|
||||
public static getSerializationInfo(component: Component | ComponentType): {
|
||||
static getSerializationInfo(component: Component | ComponentType): {
|
||||
type: string;
|
||||
version: number;
|
||||
fields: string[];
|
||||
ignoredFields: string[];
|
||||
isSerializable: boolean;
|
||||
} | null {
|
||||
} {
|
||||
const metadata = getSerializationMetadata(component);
|
||||
|
||||
if (!metadata) {
|
||||
return {
|
||||
type: 'unknown',
|
||||
version: 0,
|
||||
fields: [],
|
||||
ignoredFields: [],
|
||||
isSerializable: false
|
||||
};
|
||||
return { type: 'unknown', version: 0, fields: [], ignoredFields: [], isSerializable: false };
|
||||
}
|
||||
|
||||
const componentType = typeof component === 'function'
|
||||
@@ -367,50 +134,18 @@ export class ComponentSerializer {
|
||||
return {
|
||||
type: metadata.options.typeId || getComponentTypeName(componentType),
|
||||
version: metadata.options.version,
|
||||
fields: Array.from(metadata.fields.keys()).map((k) =>
|
||||
typeof k === 'symbol' ? k.toString() : k
|
||||
),
|
||||
ignoredFields: Array.from(metadata.ignoredFields).map((k) =>
|
||||
typeof k === 'symbol' ? k.toString() : k
|
||||
),
|
||||
fields: Array.from(metadata.fields.keys()).map(k => typeof k === 'symbol' ? k.toString() : k),
|
||||
ignoredFields: Array.from(metadata.ignoredFields).map(k => typeof k === 'symbol' ? k.toString() : k),
|
||||
isSerializable: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化 Entity 引用
|
||||
*
|
||||
* Serialize an Entity reference to a portable format.
|
||||
*
|
||||
* @param entity Entity 实例或 null
|
||||
* @returns 序列化的引用格式
|
||||
*/
|
||||
public static serializeEntityRef(entity: Entity | null): SerializableValue {
|
||||
if (!entity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
__entityRef: {
|
||||
id: entity.id,
|
||||
guid: entity.persistentId
|
||||
}
|
||||
};
|
||||
static serializeEntityRef(entity: Entity | null): SerializableValue {
|
||||
if (!entity) return null;
|
||||
return { __entityRef: { id: entity.id, guid: entity.persistentId } };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查值是否为序列化的 EntityRef
|
||||
*
|
||||
* Check if a value is a serialized EntityRef.
|
||||
*
|
||||
* @param value 要检查的值
|
||||
* @returns 如果是 EntityRef 返回 true
|
||||
*/
|
||||
public static isSerializedEntityRef(value: unknown): value is { __entityRef: SerializedEntityRef } {
|
||||
return (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
'__entityRef' in value
|
||||
);
|
||||
static isSerializedEntityRef(value: unknown): value is { __entityRef: SerializedEntityRef } {
|
||||
return typeof value === 'object' && value !== null && '__entityRef' in value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { BinarySerializer } from '../../Utils/BinarySerializer';
|
||||
import { HierarchySystem } from '../Systems/HierarchySystem';
|
||||
import { HierarchyComponent } from '../Components/HierarchyComponent';
|
||||
import { SerializationContext } from './SerializationContext';
|
||||
import { ValueSerializer, SerializableValue } from './ValueSerializer';
|
||||
|
||||
/**
|
||||
* 场景序列化格式
|
||||
@@ -387,131 +388,21 @@ export class SceneSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化场景自定义数据
|
||||
*
|
||||
* 将 Map<string, any> 转换为普通对象
|
||||
*/
|
||||
private static serializeSceneData(sceneData: Map<string, any>): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
|
||||
private static serializeSceneData(sceneData: Map<string, unknown>): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {};
|
||||
for (const [key, value] of sceneData) {
|
||||
result[key] = this.serializeValue(value);
|
||||
result[key] = ValueSerializer.serialize(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化场景自定义数据
|
||||
*
|
||||
* 将普通对象还原为 Map<string, any>
|
||||
*/
|
||||
private static deserializeSceneData(
|
||||
data: Record<string, any>,
|
||||
targetMap: Map<string, any>
|
||||
): void {
|
||||
private static deserializeSceneData(data: Record<string, unknown>, targetMap: Map<string, unknown>): void {
|
||||
targetMap.clear();
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
targetMap.set(key, this.deserializeValue(value));
|
||||
targetMap.set(key, ValueSerializer.deserialize(value as SerializableValue));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化单个值
|
||||
*/
|
||||
private static serializeValue(value: any): any {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Date
|
||||
if (value instanceof Date) {
|
||||
return { __type: 'Date', value: value.toISOString() };
|
||||
}
|
||||
|
||||
// Map
|
||||
if (value instanceof Map) {
|
||||
return { __type: 'Map', value: Array.from(value.entries()) };
|
||||
}
|
||||
|
||||
// Set
|
||||
if (value instanceof Set) {
|
||||
return { __type: 'Set', value: Array.from(value) };
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => this.serializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.serializeValue(value[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 其他类型不序列化
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化单个值
|
||||
*/
|
||||
private static deserializeValue(value: any): any {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 处理特殊类型标记
|
||||
if (type === 'object' && value.__type) {
|
||||
switch (value.__type) {
|
||||
case 'Date':
|
||||
return new Date(value.value);
|
||||
case 'Map':
|
||||
return new Map(value.value);
|
||||
case 'Set':
|
||||
return new Set(value.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => this.deserializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.deserializeValue(value[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤要序列化的实体和组件
|
||||
*/
|
||||
|
||||
113
packages/core/src/ECS/Serialization/ValueSerializer.ts
Normal file
113
packages/core/src/ECS/Serialization/ValueSerializer.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 值序列化器
|
||||
*
|
||||
* Value serializer with circular reference detection and extensible type handlers.
|
||||
*/
|
||||
|
||||
export type PrimitiveValue = string | number | boolean | null | undefined;
|
||||
|
||||
export type SerializableValue =
|
||||
| PrimitiveValue
|
||||
| SerializableValue[]
|
||||
| { readonly [key: string]: SerializableValue }
|
||||
| { readonly __type: string; readonly value: unknown };
|
||||
|
||||
type Serializer<T> = (value: T, serialize: (v: unknown) => SerializableValue) => SerializableValue;
|
||||
type Deserializer<T> = (data: { __type: string; value: unknown }) => T;
|
||||
|
||||
interface TypeDef<T = unknown> {
|
||||
check: (value: unknown) => value is T;
|
||||
serialize: Serializer<T>;
|
||||
deserialize: Deserializer<T>;
|
||||
}
|
||||
|
||||
const types = new Map<string, TypeDef>();
|
||||
|
||||
function registerType<T>(name: string, def: TypeDef<T>): void {
|
||||
types.set(name, def as TypeDef);
|
||||
}
|
||||
|
||||
// 内置类型
|
||||
registerType<Date>('Date', {
|
||||
check: (v): v is Date => v instanceof Date,
|
||||
serialize: (v) => ({ __type: 'Date', value: v.toISOString() }),
|
||||
deserialize: (d) => new Date(d.value as string)
|
||||
});
|
||||
|
||||
registerType<Map<unknown, unknown>>('Map', {
|
||||
check: (v): v is Map<unknown, unknown> => v instanceof Map,
|
||||
serialize: (v, ser) => ({ __type: 'Map', value: [...v].map(([k, val]) => [ser(k), ser(val)]) }),
|
||||
deserialize: (d) => new Map(d.value as Array<[unknown, unknown]>)
|
||||
});
|
||||
|
||||
registerType<Set<unknown>>('Set', {
|
||||
check: (v): v is Set<unknown> => v instanceof Set,
|
||||
serialize: (v, ser) => ({ __type: 'Set', value: [...v].map(ser) }),
|
||||
deserialize: (d) => new Set(d.value as unknown[])
|
||||
});
|
||||
|
||||
function serialize(value: unknown, seen = new WeakSet<object>()): SerializableValue {
|
||||
if (value == null) return value as null | undefined;
|
||||
|
||||
const t = typeof value;
|
||||
if (t === 'string' || t === 'number' || t === 'boolean') return value as PrimitiveValue;
|
||||
if (t === 'function') return undefined;
|
||||
|
||||
const obj = value as object;
|
||||
if (seen.has(obj)) return undefined;
|
||||
seen.add(obj);
|
||||
|
||||
for (const [, def] of types) {
|
||||
if (def.check(value)) {
|
||||
return def.serialize(value, (v) => serialize(v, seen));
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((v) => serialize(v, seen));
|
||||
}
|
||||
|
||||
const result: Record<string, SerializableValue> = {};
|
||||
for (const k of Object.keys(value as object)) {
|
||||
result[k] = serialize((value as Record<string, unknown>)[k], seen);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function deserialize(value: SerializableValue): unknown {
|
||||
if (value == null) return value;
|
||||
|
||||
const t = typeof value;
|
||||
if (t === 'string' || t === 'number' || t === 'boolean') return value;
|
||||
|
||||
if (isTypedValue(value)) {
|
||||
const def = types.get(value.__type);
|
||||
return def ? def.deserialize(value) : value;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(deserialize);
|
||||
}
|
||||
|
||||
const result: Record<string, unknown> = {};
|
||||
for (const k of Object.keys(value)) {
|
||||
result[k] = deserialize((value as Record<string, SerializableValue>)[k]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isTypedValue(v: unknown): v is { __type: string; value: unknown } {
|
||||
if (v === null || typeof v !== 'object') {
|
||||
return false;
|
||||
}
|
||||
return '__type' in v;
|
||||
}
|
||||
|
||||
export const ValueSerializer = {
|
||||
serialize,
|
||||
deserialize,
|
||||
register: registerType
|
||||
} as const;
|
||||
|
||||
export type { TypeDef as TypeHandler };
|
||||
export type TypedValue = { readonly __type: string; readonly value: unknown };
|
||||
@@ -24,6 +24,10 @@ export type {
|
||||
SerializationMetadata
|
||||
} from './SerializationDecorators';
|
||||
|
||||
// 值序列化器
|
||||
export { ValueSerializer } from './ValueSerializer';
|
||||
export type { SerializableValue, TypeHandler, TypedValue } from './ValueSerializer';
|
||||
|
||||
// 组件序列化器
|
||||
export { ComponentSerializer } from './ComponentSerializer';
|
||||
export type { SerializedComponent } from './ComponentSerializer';
|
||||
|
||||
Reference in New Issue
Block a user