Firestore 不支持 JavaScript 个具有自定义原型的对象?

Firestore doesn't support JavaScript objects with custom prototypes?

我正在使用节点 Bigquery Package,来 运行 一个简单的工作。查看作业的结果(比如 data),effective_date 属性如下所示:

 effective_date: BigQueryDate { value: '2015-10-02' }

这显然是返回的 data 对象中的一个对象。

将返回的 json 导入 Firestore 会出现以下错误:

UnhandledPromiseRejectionWarning: Error: Argument "data" is not a 
valid Document. Couldn't serialize object of type "BigQueryDate". 
Firestore doesn't support JavaScript objects with custom prototypes 
(i.e. objects that were created via the 'new' operator).

有没有优雅的方法来处理这个问题?是否需要遍历结果并转换/删除所有对象?

firestore Node.js 客户端不支持自定义序列化 类。

您将在本期找到更多解释:
https://github.com/googleapis/nodejs-firestore/issues/143
"We explicitly decided to not support serialization of custom classes for the Web and Node.JS client"

一种解决方案是将嵌套对象转换为普通对象。例如通过使用 lodash 或 JSON.stringify.

firestore.collection('collectionName')
    .doc('id')
    .set(JSON.parse(JSON.stringify(myCustomObject)));

这是一个相关的 post:

另一种资源消耗较少的方法:

firestore
  .collection('collectionName')
  .doc('id')
  .set(Object.assign({}, myCustomObject));

注意:它仅适用于没有嵌套对象的对象。

您也可以使用 class-transformer and it's classToPlain() along with exposeUnsetFields option 来省略 undefined 个值。

npm install class-transformer
or
yarn add class-transformer
import {classToPlain} from 'class-transformer';

firestore
  .collection('collectionName')
  .doc('id')
  .set(classToPlain(myCustomObject, {exposeUnsetFields: false}));

如果您有 FirebaseFirestore.Timestamp 对象,请不要使用 JSON.parse(JSON.stringify(obj))classToPlain(obj),因为它们会在存储到 Firestore 时损坏它。

最好用{...obj}方法

firestore
  .collection('collectionName')
  .doc('id')
  .set({...obj});

注意:不要对文档 class 中的任何嵌套对象使用 new 运算符,它不会起作用。相反,为嵌套对象属性创建 interfacetype,如下所示:

interface Profile {
    firstName: string;
    lastName: string;
}

class User {
    id = "";
    isPaid = false;
    profile: Profile = {
        firstName: "",
        lastName: "",
    };
}

const user = new User();

user.profile.firstName = "gorv";

await firestore.collection("users").add({...user});

如果你真的想存储 class 对象由深度嵌套的更多 class 对象组成,那么使用这个函数首先将它转换为普通对象,同时保留 FirebaseFirestore.Timestamp 方法。

const toPlainFirestoreObject = (o: any): any => {
  if (o && typeof o === "object" && !Array.isArray(o) && !isFirestoreTimestamp(o)) {
    return {
      ...Object.keys(o).reduce(
        (a: any, c: any) => ((a[c] = toPlainFirestoreObject(o[c])), a),
        {}
      ),
    };
  }
  return o;
};

function isFirestoreTimestamp(o: any): boolean {
  if (o && 
    Object.getPrototypeOf(o).toMillis &&
    Object.getPrototypeOf(o).constructor.name === "Timestamp"
  ) {
    return true;
  }
  return false;
}


const user = new User();

user.profile = new Profile();

user.profile.address = new Address();

await firestore.collection("users").add(toPlainFirestoreObject(user));