原因:`object`("[object Date]")无法被序列化为 JSON。请只返回可被序列化为 JSON 的数据类型。

22
我正在使用Prisma和Next.js。当我尝试在getStaticProps中从Prisma检索内容时,它确实获取了数据,但我无法将其传递给主组件。
export const getStaticProps = async () => {
  const prisma = new PrismaClient();
  const newsLetters = await prisma.newsLetters.findMany();
  console.log(newsLetters);

  return {
    props: {
      newsLetters: newsLetters,
    },
  };
};

正如您在此图像中所看到的,它正在获取并打印内容。

enter image description here

但是当我将它作为props传递时,会出现以下错误

Reason: `object` ("[object Date]") cannot be serialized as JSON. Please only return JSON serializable data types.
6个回答

28

如果您正在使用 typescript,则不能将 createdAt 的类型更改为字符串或数字,如下所示:

newsLetter.createdAt = newsLetter.createdAt.toString();
// Error: Type 'string' is not assignable to type 'Date'.

相反,你可以在JSON.parse内部使用JSON.stringify来创建可序列化的对象:

export const getStaticProps = async () => {
  const prisma = new PrismaClient();
  const newsLetters = await prisma.newsLetters.findMany();

  return {
     props: {
        newsLetters: JSON.parse(JSON.stringify(newsLetters)) // <===
     }
  }
}

1
虽然不够漂亮,但却很实用。感谢这个解决方法。 - philippe_b

9
你可以使用 Blitz的superjson来使它工作。他们在https://github.com/blitz-js/superjson#using-with-nextjs有说明:

Using with Next.js

The getServerSideProps, getInitialProps, and getStaticProps data hooks provided by Next.js do not allow you to transmit Javascript objects like Dates. It will error unless you convert Dates to strings, etc.

Thankfully, Superjson is a perfect tool to bypass that limitation!

Next.js SWC Plugin (experimental, v12.2 or above)

Next.js SWC plugins are experimental, but promise a significant speedup. To use the SuperJSON SWC plugin, install it and add it to your next.config.js:

yarn add next-superjson-plugin
// next.config.js
module.exports = {
  experimental: {
    swcPlugins: [
      [
        'next-superjson-plugin',
        {
          excluded: [],
        },
      ],
    ],
  },
} 

谢谢!不过在服务器开始运行前,我得重启了几次。 - Pencilcheck

8

看起来 NextJS 为了性能原因不支持序列化除标量类型以外的任何内容。你可以在这个Github issue中了解更多信息。最好的处理方法是在返回它们之前将日期对象转换为 UNIX 时间戳。

// your data
let newsLetters = [
    {
        id: 'your-id',
        email: 'email@example.com',
        createdAt: new Date()
    }
];

// map the array
newsLetters.map(x => {
    x.createdAt = Math.floor(x.createdAt / 1000);
    return x;
})

// use newsLetters now
console.log(newsLetters);

如果您不需要编辑日期(可能不需要创建日期),则可以将其转换为可读字符串,而不是Unix时间戳。 newsLetters.map(x => { x.createdAt = x.createdAt.toString() return x; }) - Greggory Wiley

2
根据NextJS API文档,getStaticProps返回的“应该是可序列化的对象,以便传递的任何道具都可以使用JSON.stringify进行序列化。”
在底层,它们允许布尔值、数字、字符串和通过Lodash isPlainObject测试的任何内容。在Lodash文档中,该函数声称“检查值是否为纯对象,即由Object构造函数创建的对象或具有null的[[Prototype]]的对象。”
以下堆栈帖子讨论了区别。 JavaScript中对象和纯对象之间的区别? 在@Viktor Kynchev的答案基础上,根据您从道具中需要什么,您可以将其转换为字符串、数字或Lodash的isPlainObject接受的其他类型。
对于我来说,我通过Prisma API提供了一个日期对象,就像OP一样,我只是将其转换为字符串。
for (const element of newsLetters) {
  element.createdAt = element.createdAt.toString()
}

3
你提出的转换代码片段无法运行,因为 element.createdAt 仍然是 DateTime 类型,你无法将一个字符串赋值给它。 - L0g1x
@L0g1x,这对我很有用,因为我经常需要它,所以我将其放在帮助函数中并重复使用。在什么情况下它没有起作用?也许你正在使用不同的DateTime对象? - Greggory Wiley
1
@GreggoryWiley 我认为L0g1x正在使用TypeScript。 - Mahmoud

1

你很可能在模型的某个字段中有这个

createdAt DateTime @default(now())

日期对象不可序列化。您可以使用date-fns npm模块

import { formatDistance } from 'date-fns'

const newsLetters = await prisma.newsLetters.findMany();
const serializedNesLetters= newsLetters.map((newsLetter)=>({
     ...newsLetter, 
     createdAt:formatDistance(new Date(newsLetter.timestamp),new Date())
}))

这将以文字形式返回给定日期之间的距离。

formatDistance文档


0
将您的日期值转换为某个字符串值,就像这样:

export async function getStaticProps() {
  const storeInfo = await getStoreInfo()

  return {
    props: {
      storeInfo: {
        ...storeInfo,
        createdAt: storeInfo?.createdAt.toISOString(),
        updatedAt: storeInfo?.updatedAt.toISOString(),
      },
    },
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接