192 lines
6.1 KiB
TypeScript
192 lines
6.1 KiB
TypeScript
import { CaseConvention, convertCase } from './case'
|
|
import { Deserializer } from './de'
|
|
import { Serializer } from './ser'
|
|
import { isFunction, isNumber, isString } from './utils'
|
|
import { GlobalRegistry, Registry } from './registry'
|
|
|
|
|
|
export interface RenameOptions {
|
|
serialize?: string
|
|
deserialize?: string
|
|
}
|
|
|
|
export interface RenameAllOptions {
|
|
serialize?: CaseConvention
|
|
deserialize?: CaseConvention
|
|
}
|
|
|
|
export interface ContainerOptions {
|
|
// deserialization only
|
|
default?: () => any
|
|
// deserialization only
|
|
denyUnknownFields?: boolean
|
|
expecting?: string
|
|
rename?: RenameOptions | string
|
|
renameAll?: RenameAllOptions | CaseConvention
|
|
registry?: Registry
|
|
tag?: string
|
|
content?: string
|
|
untagged?: boolean
|
|
}
|
|
|
|
export interface ConditionalSkipOptions {
|
|
if: (value: any) => boolean
|
|
}
|
|
|
|
export interface SkipOptions {
|
|
serializing?: ConditionalSkipOptions | boolean
|
|
deserializing?: ConditionalSkipOptions | boolean
|
|
}
|
|
|
|
export type CustomSerializer = <T, V, S extends Serializer<T>>(value: V, serializer: S) => T
|
|
export type CustomDeserializer = <T, D extends Deserializer>(deserializer: D) => T
|
|
|
|
export interface PropertyOptions {
|
|
alias?: string
|
|
// deserialization only
|
|
default?: () => any
|
|
flatten?: boolean
|
|
rename?: RenameOptions | string
|
|
//serializeWith?: CustomSerializer
|
|
//deserializeWith: CustomDeserializer
|
|
skip?: SkipOptions | ConditionalSkipOptions | boolean
|
|
}
|
|
|
|
export const Stage = Object.freeze({
|
|
Serialize: 0,
|
|
Deserialize: 1
|
|
} as const)
|
|
|
|
export type Stage = typeof Stage[keyof typeof Stage]
|
|
|
|
export class SerdeOptions {
|
|
target: any
|
|
readonly options: ContainerOptions
|
|
readonly properties: Map<string, PropertyOptions>
|
|
|
|
get registry() {
|
|
return this.options.registry || GlobalRegistry
|
|
}
|
|
|
|
constructor(options: ContainerOptions = {}, properties: Map<string, PropertyOptions> = new Map()) {
|
|
this.options = options
|
|
this.properties = properties
|
|
}
|
|
|
|
static from(target: any) {
|
|
return new this(target)
|
|
}
|
|
|
|
getClassName(stage: Stage) {
|
|
if (isString(this.options.rename)) {
|
|
return this.options.rename
|
|
} else if (stage === Stage.Serialize && isString(this.options.rename?.serialize)) {
|
|
return this.options.rename.serialize
|
|
} else if (stage === Stage.Deserialize && isString(this.options.rename?.deserialize)) {
|
|
return this.options.rename.deserialize
|
|
} else {
|
|
return this.target.constructor.name
|
|
}
|
|
}
|
|
|
|
private getPropertyRename(property: string, stage: Stage, options: PropertyOptions) {
|
|
if (options != null) {
|
|
if (isString(options.rename)) {
|
|
return options.rename
|
|
} else if (stage === Stage.Serialize && isString(options.rename?.serialize)) {
|
|
return options.rename.serialize
|
|
} else if (stage === Stage.Deserialize && isString(options.rename?.deserialize)) {
|
|
return options.rename.deserialize
|
|
}
|
|
}
|
|
|
|
return property
|
|
}
|
|
|
|
private getPropertyCase(name: string, stage: Stage) {
|
|
if (isNumber(this.options.renameAll)) {
|
|
return convertCase(name, this.options.renameAll)
|
|
} else if (stage === Stage.Serialize && isNumber(this.options.renameAll?.serialize)) {
|
|
return convertCase(name, this.options.renameAll.serialize)
|
|
} else if (stage === Stage.Deserialize && isNumber(this.options.renameAll?.deserialize)) {
|
|
return convertCase(name, this.options.renameAll.deserialize)
|
|
} else {
|
|
return name
|
|
}
|
|
}
|
|
|
|
getPropertyName(property: string, stage: Stage) {
|
|
const options = this.properties.get(property)
|
|
const name = options != null ? this.getPropertyRename(property, stage, options) : property
|
|
return this.getPropertyCase(name, stage)
|
|
}
|
|
|
|
getSerializationName(property: string) {
|
|
return this.getPropertyName(property, Stage.Serialize)
|
|
}
|
|
|
|
getDeserializationName(property: string) {
|
|
return this.getPropertyName(property, Stage.Deserialize)
|
|
}
|
|
|
|
getDefault(property: string) {
|
|
const options = this.properties.get(property)
|
|
if (options && isFunction(options.default)) {
|
|
return options.default()
|
|
} else if (isFunction(this.options.default)) {
|
|
return this.options.default()
|
|
}
|
|
}
|
|
|
|
//getCustomImpl(property: string, stage: Stage) {
|
|
// const options = this.properties.get(property)
|
|
// if (options != null) {
|
|
// if (stage === Stage.Serialize && isFunction(options.serializeWith)) {
|
|
// return options.serializeWith
|
|
// } else if (stage === Stage.Deserialize && isFunction(options.deserializeWith)) {
|
|
// return options.deserializeWith
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
//getSerializer(property: string): Nullable<CustomSerializer> {
|
|
// return this.getCustomImpl(property, Stage.Serialize) as CustomSerializer
|
|
//}
|
|
|
|
//getDeserializer(property: string): Nullable<CustomDeserializer> {
|
|
// return this.getCustomImpl(property, Stage.Deserialize) as CustomDeserializer
|
|
//}
|
|
|
|
private isConditionalSkip(skip: any): skip is ConditionalSkipOptions {
|
|
return 'if' in skip && isFunction(skip.if)
|
|
}
|
|
|
|
shouldSkip(property: string, value: any, stage: Stage): boolean {
|
|
const options = this.properties.get(property)
|
|
if (options != null && options.skip != null) {
|
|
if (typeof options.skip === 'boolean') {
|
|
return options.skip
|
|
} else if (this.isConditionalSkip(options.skip)) {
|
|
return options.skip.if(value)
|
|
} else if (stage === Stage.Serialize && typeof options.skip.serializing === 'boolean') {
|
|
return options.skip.serializing
|
|
} else if (stage === Stage.Serialize && this.isConditionalSkip(options.skip.serializing)) {
|
|
return options.skip.serializing.if(value)
|
|
} else if (stage === Stage.Deserialize && typeof options.skip.deserializing === 'boolean') {
|
|
return options.skip.deserializing
|
|
} else if (stage === Stage.Deserialize && this.isConditionalSkip(options.skip.deserializing)) {
|
|
return options.skip.deserializing.if(value)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
shouldSkipSerializing(property: string, value: any): boolean {
|
|
return this.shouldSkip(property, value, Stage.Serialize)
|
|
}
|
|
|
|
shouldSkipDeserializing(property: string, value: any): boolean {
|
|
return this.shouldSkip(property, value, Stage.Deserialize)
|
|
}
|
|
}
|
|
|