UNPKG

@eclipse-scout/core

Version:
113 lines (99 loc) 5.06 kB
/* * Copyright (c) 2010, 2025 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {BaseDoEntity, Constructor, dataObjects, DoValueMetaData, doValueMetaData, InitModelOf, ObjectModel, objects, ObjectWithType, Predicate, scout} from '../../index'; export class DataObjectDeserializer implements DataObjectDeserializerModel, ObjectWithType { declare model: DataObjectDeserializerModel; protected static _TYPE_VERSION_ATTRIBUTE_NAME = '_typeVersion'; id: string; objectType: string; createPojoIfDoIsUnknown: boolean; retainTypeVersion: Predicate<object>; constructor(model?: InitModelOf<DataObjectDeserializer>) { this.createPojoIfDoIsUnknown = !!model?.createPojoIfDoIsUnknown; this.retainTypeVersion = scout.nvl(model?.retainTypeVersion, (obj: object) => !(obj instanceof BaseDoEntity)); if (!objects.isFunction(this.retainTypeVersion)) { const retain = !!this.retainTypeVersion; this.retainTypeVersion = () => retain; } } deserialize<T extends object>(value: any, valueMetaData?: DoValueMetaData<T>): T { const deserializer = dataObjects.serializers.find(d => d.canDeserialize(value, valueMetaData)); if (deserializer) { // use custom deserializer return deserializer.deserialize(value, valueMetaData, this); } if (objects.isNullOrUndefined(value)) { return value; // no value to convert } if (objects.isPojo(value)) { // nested object return this._deserializeObject(value, valueMetaData); } return value; } protected _deserializeObject<T extends object>(rawObj: Record<string, any>, metaData?: DoValueMetaData<T>): T { const constructor = this._resolveDataObjectType(rawObj, metaData) as Constructor<T>; const resultObj = this._createResultObject(constructor); const proto = Object.getPrototypeOf(constructor).prototype; Object.keys(rawObj) // Ignore _typeVersion as it is handled later on. .filter(key => key !== DataObjectDeserializer._TYPE_VERSION_ATTRIBUTE_NAME) .forEach(key => { resultObj[key] = this._convertFieldValue(proto, rawObj, key, rawObj[key]); }); const typeVersion = rawObj[DataObjectDeserializer._TYPE_VERSION_ATTRIBUTE_NAME]; if (typeVersion && this.retainTypeVersion(resultObj)) { resultObj[DataObjectDeserializer._TYPE_VERSION_ATTRIBUTE_NAME] = this._convertFieldValue(proto, rawObj, DataObjectDeserializer._TYPE_VERSION_ATTRIBUTE_NAME, typeVersion); } return resultObj; } protected _resolveDataObjectType(rawObj: Record<string, any>, metaData?: DoValueMetaData): Constructor { for (const resolver of dataObjects.doTypeResolvers) { const constructor = resolver.resolve(rawObj, metaData); if (constructor) { return constructor; } } return BaseDoEntity; } protected _createResultObject<T extends object>(constructor: Constructor<T>): T { if (this.createPojoIfDoIsUnknown && constructor === BaseDoEntity) { // DataObject could not be found and missing DOs should be created as Pojo instead of BaseDoEntity (legacy case) return {} as T; } return scout.create(constructor, null /* must always be possible to create a DO without model */); } protected _convertFieldValue(proto: object, rawObj: object, key: string, value: any): any { const fieldMetaData = doValueMetaData.getFieldMetaData(proto, key); return this.deserialize(value, fieldMetaData); } } export interface DataObjectDeserializerModel extends ObjectModel<DataObjectDeserializer> { /** * Controls the kind of object that will be created when deserializing unknown DataObjects. * If `true` a pojo will be created for unknown DOs. If `false`, instances of {@link BaseDoEntity} will be created. * Changing the type of the deserialized DataObject may change the deserialization behaviour of the property '_typeVersion' (see {@link retainTypeVersion} for more details). * Default is `false`. * * A DataObject can be unknown e.g. if: * <ol> * <li>The _type attribute of the object to deserialize cannot be found in TS.</li> * <li>There is no _type attribute and the TypeScript attribute declaration (metadata) does not provide a class type which could be used as fallback.</li> * </ol> */ createPojoIfDoIsUnknown?: boolean; /** * Controls whether the '_typeVersion' property is retained when deserializing DataObjects. * The property can be given as a {@link boolean} or a {@link Predicate}, whose input is the deserialized DataObject. * If `true`, the '_typeVersion' will be set on the deserialized DataObject. If `false`, it will be ignored. * Default is a {@link Predicate} that ignores the '_typeVersion' if the deserialized DataObject is instance of {@link BaseDoEntity}. */ retainTypeVersion?: boolean | Predicate<object>; }