下面我将为你实现一个完整的 Angular 自动序列化/反序列化装饰器,它可以处理类与普通对象之间的转换,支持嵌套对象、日期处理和自定义转换逻辑。
完整代码实现
1. 首先创建核心装饰器和工具函数
typescript
// serializable.decorator.ts
export interface SerializableConfig {
alias?: string;
customConverter?: (value: any) => any;
type?: any;
}
// 缓存元数据
const serializableMetadataKey = Symbol('serializable:metadata');
export function Serializable(config: SerializableConfig = {}): PropertyDecorator {
return (target: any, propertyKey: string | symbol) => {
const metadata = Reflect.getMetadata(serializableMetadataKey, target) || {};
metadata[propertyKey] = {
alias: config.alias || propertyKey,
customConverter: config.customConverter,
type: config.type
};
Reflect.defineMetadata(serializableMetadataKey, metadata, target);
};
}
export function getSerializableMetadata(target: any): {[key: string]: SerializableConfig} {
return Reflect.getMetadata(serializableMetadataKey, target) || {};
}
// 处理不同类型的转换
function convertValue(value: any, type: any): any {
if (value === null || value === undefined) {
return value;
}
// 处理数组
if (type && type.prototype && Array.isArray(type.prototype)) {
return value.map((item: any) => convertValue(item, type));
}
// 处理日期
if (type === Date) {
return new Date(value);
}
// 处理嵌套对象
if (typeof type === 'function' && type.prototype && type.prototype.constructor) {
return deserialize(value, type);
}
return value;
}
// 反序列化函数
export function deserialize<T>(json: any, type: new () => T): T {
if (!json) {
return json;
}
const instance = new type();
const metadata = getSerializableMetadata(instance);
Object.keys(metadata).forEach(propertyKey => {
const config = metadata[propertyKey];
const jsonKey = config.alias;
const value = json[jsonKey];
if (value !== undefined) {
if (config.customConverter) {
instance[propertyKey] = config.customConverter(value);
} else if (config.type) {
instance[propertyKey] = convertValue(value, config.type);
} else {
instance[propertyKey] = value;
}
}
});
return instance;
}
// 序列化函数
export function serialize(instance: any): any {
if (!instance) {
return instance;
}
const result: any = {};
const metadata = getSerializableMetadata(instance);
Object.keys(metadata).forEach(propertyKey => {
const config = metadata[propertyKey];
const value = instance[propertyKey];
if (value !== undefined) {
if (config.customConverter) {
result[config.alias] = config.customConverter(value);
} else if (value instanceof Date) {
result[config.alias] = value.toISOString();
} else if (Array.isArray(value)) {
result[config.alias] = value.map(item => serialize(item));
} else if (typeof value === 'object' && value !== null) {
result[config.alias] = serialize(value);
} else {
result[config.alias] = value;
}
}
});
return result;
}
2. 创建测试模型和使用示例
typescript
// user.model.ts
import { Serializable, deserialize, serialize } from './serializable.decorator';
class Address {
@Serializable()
street: string;
@Serializable()
city: string;
@Serializable({ alias: 'zip_code' })
zipCode: string;
}
class User {
@Serializable({ alias: 'user_id' })
id: number;
@Serializable()
name: string;
@Serializable({ alias: 'created_at', type: Date })
createdAt: Date;
@Serializable({ type: Address })
address: Address;
@Serializable({
alias: 'is_active',
customConverter: (value: any) => Boolean(value)
})
isActive: boolean;
@Serializable({ type: [String] })
tags: string[];
}
// 使用示例
const jsonData = {
user_id: 123,
name: 'John Doe',
created_at: '2023-05-15T10:00:00Z',
address: {
street: '123 Main St',
city: 'New York',
zip_code: '10001'
},
is_active: 1,
tags: ['premium', 'vip']
};
// 反序列化
const user = deserialize(jsonData, User);
console.log(user);
/*
User {
id: 123,
name: 'John Doe',
createdAt: Date,
address: Address {
street: '123 Main St',
city: 'New York',
zipCode: '10001'
},
isActive: true,
tags: ['premium', 'vip']
}
*/
// 序列化
const serializedUser = serialize(user);
console.log(serializedUser);
/*
{
user_id: 123,
name: 'John Doe',
created_at: '2023-05-15T10:00:00.000Z',
address: {
street: '123 Main St',
city: 'New York',
zip_code: '10001'
},
is_active: true,
tags: ['premium', 'vip']
}
*/
3. 在 Angular 服务中使用
typescript
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { deserialize, serialize } from './serializable.decorator';
import { User } from './user.model';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) {}
getUser(id: number) {
return this.http.get(`/api/users/${id}`).pipe(
map(response => deserialize(response, User))
);
}
updateUser(user: User) {
return this.http.put(`/api/users/${user.id}`, serialize(user));
}
}
功能说明
-
@Serializable 装饰器:
alias: 指定 JSON 中的属性名与类属性名不同时的映射type: 指定属性的类型,用于复杂类型的自动转换customConverter: 自定义转换函数
-
支持的类型:
- 基本类型 (string, number, boolean)
- 日期对象 (自动与 ISO 字符串转换)
- 嵌套对象
- 数组
- 自定义类型
-
主要函数:
deserialize(): 将普通对象转换为类实例serialize(): 将类实例转换为普通对象
使用建议
-
在 Angular 项目中,可以将此装饰器用于 API 返回的数据模型
-
在 HTTP 拦截器中自动处理序列化/反序列化
-
对于大型项目,可以考虑进一步扩展支持:
- 循环引用处理
- 更复杂的自定义验证
- 继承关系的处理
这个实现提供了完整的类型安全性和灵活的配置选项,可以满足大多数 Angular 项目中对象序列化的需求。