diff --git a/dist/de/generic.d.ts b/dist/de/generic.d.ts index 3db19cd..6fe9bfb 100644 --- a/dist/de/generic.d.ts +++ b/dist/de/generic.d.ts @@ -11,5 +11,6 @@ export declare class GenericVisitor implements Visitor { visitSymbol(value: symbol): T; visitNull(): T; visitObject(access: MapAccess): T; + visitClass(name: string, value: MapAccess): T; visitIterable(access: IterableAccess): T; } diff --git a/dist/de/generic.js b/dist/de/generic.js index 43c3286..e00c467 100644 --- a/dist/de/generic.js +++ b/dist/de/generic.js @@ -91,6 +91,10 @@ class GenericVisitor { } return result; } + visitClass(name, value) { + console.log(name, '!!!!!!'); + return this.visitObject(value); + } visitIterable(access) { const result = new Array(access.sizeHint()); let element; diff --git a/dist/de/impl.js b/dist/de/impl.js index 4493749..629567d 100644 --- a/dist/de/impl.js +++ b/dist/de/impl.js @@ -6,7 +6,7 @@ const generic_1 = require("./generic"); function deserializeWith(deserializer, into, options) { const visitor = new generic_1.GenericVisitor(); const target = new into(); - const obj = deserializer.deserializeObject(visitor); + const obj = deserializer.deserializeClass(into.name, visitor); const newObject = {}; for (const property in target) { const name = options.getPropertyName(property, options_1.Stage.Deserialize); diff --git a/dist/de/interface.d.ts b/dist/de/interface.d.ts index aca1ddf..9f635f0 100644 --- a/dist/de/interface.d.ts +++ b/dist/de/interface.d.ts @@ -33,6 +33,7 @@ export interface Visitor { visitSymbol(value: symbol): T; visitNull(): T; visitObject(value: MapAccess): T; + visitClass(name: string, value: MapAccess): T; visitIterable?(value: IterableAccess): T; } export interface Deserializer { @@ -44,6 +45,7 @@ export interface Deserializer { deserializeSymbol>(visitor: V): T; deserializeNull>(visitor: V): T; deserializeObject>(visitor: V): T; + deserializeClass>(name: string, visitor: V): T; deserializeIterable>(visitor: V): T; } export interface Deserialize { diff --git a/dist/options.d.ts b/dist/options.d.ts index d38ad43..310acf9 100644 --- a/dist/options.d.ts +++ b/dist/options.d.ts @@ -31,7 +31,7 @@ export interface SkipOptions { export type CustomSerializer = >(value: V, serializer: S) => T; export type CustomDeserializer = (deserializer: D) => T; export interface PropertyOptions { - alias?: string; + aliases?: Set; default?: () => any; flatten?: boolean; rename?: RenameOptions | string; diff --git a/dist/ser/impl.js b/dist/ser/impl.js index c571ae2..17a6543 100644 --- a/dist/ser/impl.js +++ b/dist/ser/impl.js @@ -17,8 +17,13 @@ function serializeEntries(serializer, value, options) { } return serializer.end(); } +function serializeClass(serializer, value, options) { + const ser = serializer.serializeClass(value.constructor.name); + return serializeEntries(ser, Object.entries(value), options); +} function serializeObject(serializer, value, options) { - return serializeEntries(serializer.serializeObject(), Object.entries(value), options); + const ser = serializer.serializeObject(); + return serializeEntries(ser, Object.entries(value), options); } function serializeIter(serializer, value, options) { let state; @@ -47,6 +52,9 @@ function serializeWith(serializer, value, optionsGetter = defaultOptions) { if ((0, utils_1.isIterable)(value) && (0, utils_1.isFunction)(serializer.serializeIterable)) { return serializeIter(serializer.serializeIterable(), value, options); } + else if (!(0, utils_1.isPlainObject)(value)) { + return serializeClass(serializer, value, options); + } else if ((0, utils_1.isFunction)(serializer.serializeObject)) { return serializeObject(serializer, value, options); } // deliberate fallthrough when the above fail diff --git a/dist/ser/interface.d.ts b/dist/ser/interface.d.ts index b4a028c..af8741b 100644 --- a/dist/ser/interface.d.ts +++ b/dist/ser/interface.d.ts @@ -17,6 +17,7 @@ interface TypeSerializer { serializeNull(): T; serializeObject(): ObjectSerializer; serializeIterable?(): IterableSerializer; + serializeClass(name: PropertyKey): ObjectSerializer; } interface AnySerializer { serializeAny?(value?: any): T; diff --git a/dist/ser/interface.js b/dist/ser/interface.js index b0fe4a5..d0a2e57 100644 --- a/dist/ser/interface.js +++ b/dist/ser/interface.js @@ -12,7 +12,7 @@ const TypeSerializerMethods = [ 'serializeIterable', 'serializeNull', 'serializeObject', - //'serializeClass', + 'serializeClass', ]; const AnySerializerMethods = ['serializeAny']; function isGenericSerializer(value) { diff --git a/src/de/generic.ts b/src/de/generic.ts index 24ec9ff..52d5080 100644 --- a/src/de/generic.ts +++ b/src/de/generic.ts @@ -48,6 +48,12 @@ export class GenericVisitor implements Visitor { return result } + // TODO: use global registry to deserialize classes + visitClass(name: string, access: MapAccess): T { + console.log(name, '!!!!!!') + return this.visitObject(access) + } + visitIterable(access: IterableAccess): T { const result = new Array(access.sizeHint()) let element diff --git a/src/de/impl.ts b/src/de/impl.ts index 3802642..97e7f5a 100644 --- a/src/de/impl.ts +++ b/src/de/impl.ts @@ -6,8 +6,12 @@ type DeserializeConstructor = Deserialize & { new(): Deserialize } export function deserializeWith>(deserializer: D, into: E, options: SerdeOptions): T { const visitor = new GenericVisitor() + // options to figure out properties/fields during deserialization: + // offer schema decorator + // parse fields from javascript with acorn + // allow no-param constructor for initialization - this is what it does now const target = new into() - const obj = deserializer.deserializeObject(visitor) as any + const obj = deserializer.deserializeClass(into.name, visitor) as any const newObject = {} as any for (const property in target) { diff --git a/src/de/interface.ts b/src/de/interface.ts index d666455..fde42b9 100644 --- a/src/de/interface.ts +++ b/src/de/interface.ts @@ -62,6 +62,7 @@ export interface Visitor { visitSymbol(value: symbol): T visitNull(): T visitObject(value: MapAccess): T + visitClass(name: string, value: MapAccess): T visitIterable?(value: IterableAccess): T } @@ -74,6 +75,7 @@ export interface Deserializer { deserializeSymbol>(visitor: V): T deserializeNull>(visitor: V): T deserializeObject>(visitor: V): T + deserializeClass>(name: string, visitor: V): T deserializeIterable>(visitor: V): T } diff --git a/src/options.ts b/src/options.ts index c8cd724..bc858af 100644 --- a/src/options.ts +++ b/src/options.ts @@ -42,7 +42,7 @@ export type CustomSerializer = >(value: V, seriali export type CustomDeserializer = (deserializer: D) => T export interface PropertyOptions { - alias?: string + aliases?: Set // deserialization only default?: () => any flatten?: boolean diff --git a/src/ser/impl.ts b/src/ser/impl.ts index c76ecab..75a6ac4 100644 --- a/src/ser/impl.ts +++ b/src/ser/impl.ts @@ -1,6 +1,6 @@ import { IterableSerializer, ObjectSerializer, Serializable, Serializer } from './interface' import { SerdeOptions, Stage } from '../options' -import { ifNull, isFunction, isIterable, Nullable, orElse } from '../utils' +import { ifNull, isFunction, isIterable, isPlainObject, Nullable, orElse } from '../utils' const unhandledType = (serializer: any, value: any) => new TypeError(`'${serializer.constructor.name}' has no method for value type '${typeof value}'`) @@ -20,8 +20,15 @@ function serializeEntries>(serializer: Serializer, value: R, options?: SerdeOptions) { + const ser = serializer.serializeClass!(value.constructor.name) + return serializeEntries(ser, Object.entries(value) as Iterable<[K, V]>, options) +} + + function serializeObject>(serializer: Serializer, value: R, options?: SerdeOptions) { - return serializeEntries(serializer.serializeObject!(), Object.entries(value) as Iterable<[K, V]>, options) + const ser = serializer.serializeObject!() + return serializeEntries(ser, Object.entries(value) as Iterable<[K, V]>, options) } function serializeIter>(serializer: IterableSerializer, value: V, options?: SerdeOptions) { @@ -61,6 +68,8 @@ export function serializeWith(serializer: Serializer, value: Serializable, const options = optionsGetter(value) if (isIterable(value) && isFunction(serializer.serializeIterable)) { return serializeIter(serializer.serializeIterable(), value, options) + } else if (!isPlainObject(value)) { + return serializeClass(serializer, value as Record, options) } else if (isFunction(serializer.serializeObject)) { return serializeObject(serializer, value as Record, options) } // deliberate fallthrough when the above fail diff --git a/src/ser/interface.ts b/src/ser/interface.ts index 5d9996a..fd1c3f2 100644 --- a/src/ser/interface.ts +++ b/src/ser/interface.ts @@ -21,7 +21,7 @@ const TypeSerializerMethods = [ 'serializeIterable', 'serializeNull', 'serializeObject', - //'serializeClass', + 'serializeClass', ] as const interface TypeSerializer { @@ -34,7 +34,7 @@ interface TypeSerializer { serializeObject(): ObjectSerializer // serializeMap?(): ObjectSerializer serializeIterable?(): IterableSerializer - //serializeClass(name: PropertyKey): ObjectSerializer + serializeClass(name: PropertyKey): ObjectSerializer } const AnySerializerMethods = ['serializeAny']