代码之家  ›  专栏  ›  技术社区  ›  J4N

如何防止类转换器转换对象的某些字段

  •  0
  • J4N  · 技术社区  · 5 年前

    我想用图书馆 class-transformer ( https://github.com/typestack/class-transformer )将typescript类序列化到firebase,firebase不接受自定义类型。

    问题是,有些数据实际上与firebase兼容,比如“坐标”是地质点格式。

    我已经把这个类序列化了:

    export abstract class StopStep extends Step {
      id: string;
      name: string;
      description: string;
      pointOfInterest: PointOfInterest | null;
      address: string;
      coordinates: firebase.firestore.GeoPoint;
    }
    

    我想防止“坐标”字段被转换。它应该存在,只是不能未经翻译。

    我试着玩这个游戏 @Transform(({value})=>...) ,但它似乎只是一个“附加”转换,它不允许我保持相同的格式。

    我也试过了 Exclude ,但这片土地就不复存在了。

    有没有办法通过这个库来实现这一点?

    0 回复  |  直到 5 年前
        1
  •  6
  •   Mirco S.    5 年前

    答复: 我已经对整个系统进行了分析和调试 TransformOperationExecutor.ts ,简单地说,没有内置的方法来抑制这个执行者试图进行的无数转换。我在想也许可以移除 @Type 所有的装饰师都能达到这个目标。但即使缺少类型信息,该库仍会尽可能地将复杂对象转换为普通对象。原因是什么 @Transform(({value})=>...) 不起作用的是,即使对于返回的值,也会有多个检查来确定转换后的值是否需要进一步转换。” 平原 ".

    解决方法: 我看到的唯一解决方案是自己构建该功能。我可以提供以下实现来模拟 @Ignore 特点:

    import { classToPlain, ClassTransformOptions, Transform, TransformFnParams } from 'class-transformer';
    
    // New decorator to be used on members that should not be transformed.
    export function Ignore() {
        // reuse transform decorator
        return Transform((params: TransformFnParams) => {
            if (params.type == TransformationType.CLASS_TO_PLAIN) {
                // class-transformer won't touch functions,
                // so we use function-objects as container to skip transformation.
                const container = () => params.value;
                container.type = '__skipTransformContainer__';
    
                return container;
            }
            // On other transformations just return the value.
            return params.value;
        });
    }
    
    // Extended classToPlain to unwrap the container objects
    export function customClassToPlain<T>(object: T, options?: ClassTransformOptions) {
        const result = classToPlain(object, options);
        unwrapSkipTransformContainers(result);
        return result;
    }
    
    // Recursive function to iterate over all keys of an object and its nested objects.
    function unwrapSkipTransformContainers(obj: any) {
        for (const i in obj) {
            if (obj.hasOwnProperty(i)) {
                const currentValue = obj[i];
                if (currentValue?.type === '__skipTransformContainer__') {
                    // retrieve the original value and also set it.
                    obj[i] = currentValue();
                    continue;
                }
    
                // recursion + recursion anchor
                if (typeof currentValue === 'object') {
                    unwrapSkipTransformContainers(currentValue);
                }
            }
        }
    }
    

    该解决方案利用了类转换器不会转换函数(get/set函数除外),因为这些函数不是普通对象的一部分。我们重复使用 Transform decorator来包装我们不想被函数转换的成员。我们还用字符串标记这些函数 __skipTransformContainer__ 以便以后很容易找到它们,并且不会意外地调用错误的函数。然后我们写一个新的 customClassToPlain ,它首先调用默认值 classToPlain 然后对结果调用递归展开函数。展开函数被调用 unwrapSkipTransformContainer 并递归搜索所有 __skipTransformContainer__ -容器来检索未转换的值。

    用法:

    export abstract class StopStep extends Step {
      id: string;
      name: string;
      description: string;
      pointOfInterest: PointOfInterest | null;
      address: string;
      @Ignore()
      coordinates: firebase.firestore.GeoPoint;
    }
    
    class ExampleStep extends StopStep {/*logic*/}
        
    const step = new ExampleStep();
    const plain = customClassToPlain(step);
    
        2
  •  0
  •   Eray Hanoglu    5 年前

    坏消息; Micro S.是对的。“类转换器”强制应用默认转换,即使您有自定义转换函数。这限制了自定义转换函数的使用,意味着“你不能返回任何你想要的”。

    好消息; JavaScript是一种高度灵活的语言。这意味着您可以重写类原型的任何函数。下面的代码在不降低性能的情况下修复了这个问题。

    import isPlainObject from 'putil-isplainobject';
    
    const oldTransformFn = TransformOperationExecutor.prototype.transform;
    TransformOperationExecutor.prototype.transform =
      function transform(source: Record<string, any> | Record<string, any>[] | any,
                         value: Record<string, any> | Record<string, any>[] | any,
                         targetType: Function | TypeMetadata,
                         // eslint-disable-next-line @typescript-eslint/no-unused-vars
                         arrayType: Function,
                         // eslint-disable-next-line @typescript-eslint/no-unused-vars
                         isMap: boolean,
                         // eslint-disable-next-line @typescript-eslint/no-unused-vars
                         level?: number): any {
        // @ts-ignore
        if (isPlainObject(value) && targetType === Object) {
          return value;
        }
        // @ts-ignore
        return oldTransformFn.apply(this, arguments);
      };
    
    推荐文章