如何在ECMAScript 6中导入JSON文件?

354

我如何在ECMAScript 6中访问JSON文件?

以下方法无法使用:

import config from '../config.json'

如果我尝试导入JavaScript文件,则这很好用。


https://www.stefanjudis.com/snippets/how-to-import-json-files-in-es-modules-node-js/

ES模块在Node.js领域仍然相对较新(从Node 14开始稳定)。模块具有内置模块系统和顶级等待等功能。模块是带有内置模块系统的,还具有一些特性,例如顶级等待。

我读了Pawel Grzybek关于ES模块的一个有信息量的帖子并了解到今天不能在ES模块中导入JSON文件。

import info from `./package.json` assert { type: `json` };


const { default: info } = await import("./package.json", {
  assert: {
    type: "json",
  },
});

这真是太糟糕了,因为我已经习惯了在Node.js中进行require调用,例如const data = require('./some-file.json')

但是你能否在今天的Node.js中使用import assertions呢?

截至目前(最新版本v18.12),当前Node.js LTS仍将import assertions标记为实验性功能。

本文介绍如果您尚不想使用实验性功能来处理ES模块中的JSON,可以采取哪些方法。

选项1:自行读取和解析JSON文件

Node.js文档建议使用fs模块并自行完成读取文件和解析文件的工作。

import { readFile } from 'fs/promises';
const json = JSON.parse(
  await readFile(
    new URL('./some-file.json', import.meta.url)
  )
);

选项 2:利用 CommonJS 的 require 函数加载 JSON 文件

文档还指出,您可以使用 createRequire 来加载 JSON 文件。这种方法是 Pawel 在他的博客文章中建议的方法。

createRequire 允许您构建一个 CommonJS require 函数,并使用典型的 CommonJS 特性,如在 Node.js EcmaScript 模块中读取 JSON。

import { createRequire } from "module";
const require = createRequire(import.meta.url);
const data = require("./data.json");

4
这与ES6无关,而与您使用的模块加载器有关。语法本身是正确的。 - Felix Kling
2
最干净的方法是使用 webpackjson-loader - suprita shankar
25
ES6支持使用以下语法导入JSON:import * as data from './example.json'; - williamli
7
@williamli,除非有其他更多的操作(例如像Babel这样重写代码的构建步骤),否则在现今的浏览器(2020)中是不起作用的:例如,在Chrome中会出现“Failed to load module script: The server responded with a non-JavaScript MIME type of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec.”(请参见此规范讨论此处和关于此提案仓库的讨论)。目前来看,需要使用以下其中一个答案。 - ShreevatsaR
简单的答案是不要这样做。将JSON转换为JavaScript,然后保存为.mjs文件,并像往常一样进行导入。 - Ronnie Royston
24个回答

249

34
补充一点(TypeScript中导入JSON模块)的内容,现在您可以将以下代码简单地添加到tsconfig文件中...{ "compilerOptions": { "resolveJsonModule": true } } 这样做将允许您导入和使用JSON模块。 - Matt Coady
7
有哪些浏览器支持这个功能?我在最新版的 Firefox 上尝试,但收到了错误信息:“Loading failed for the module with source "example.json"”;在 Chrome 上则提示:“Failed to load module script: The server responded with a non-JavaScript MIME type of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec." - Coderer
2
我突然想到,当你说“在ES6中”时,实际上你是指“在TS中”--我以为你在谈论tsc发出的代码,但实际上情况并非如此。我正在尝试在浏览器上原生运行ES6模块(<script type="module">),如果直接运行那个import行,就会出现上面的错误。如果我错了,请纠正我,如果你确实意味着在实际的ES6中可以从JSON中导入。 - Coderer
8
当我说“有没有任何浏览器支持这个”,我并不是指“在通过Babel转译之后”。你提供的第一篇文章展示了TS将import语句转译为Node.js的require()(试一下!),而第二篇文章则没有提到关于JSON导入的信息。这里的问题不在于解析器或者模块系统,而在于“加载器”——浏览器的加载器只会为JavaScript解析导入。实际上,在底层,即使您的构建工具链将它们抽象出来,您始终需要使用AJAX调用(fetch / XHR)并解析结果。 - Coderer
1
为了不忘记当我们兴奋时,转译器所做的就是将JSON内容简单地内联到输出文件中的常量中,因为在JavaScript世界中,这确实是唯一安全的方式(除了Ajax,这是另一个故事)。 - Ayyash
显示剩余2条评论

167

使用ES模块导入JSON在2020年中期被提交给TC39作为一个新特性,目前(在此编辑时)它处于第三阶段,这是在规范中被接受之前的最后阶段(更多细节请参见https://github.com/tc39/proposal-json-modules)。一旦实现,您可以像这样使用它:

import someName from "./some/path/to/your/file.json";

someName是JS对象中对应JSON数据的变量名。需要注意的是,这里从JSON源导入了JavaScript,而不是"导入JSON"。

如果你使用的是足够现代的打包工具(如esbuild等),或者你正在使用足够新的转译器(如babel),那么就可以直接使用此语法,无需担心兼容性问题。

另外,如果你有权限修改JSON文件,也可以通过添加一些代码,将JSON转换为有效的JS文件:

config.js

export default
{
  // my json here...
}

那么...

import config from '../config.js'

不支持导入现有的 .json 文件,但功能还算不错。


278
这并没有实际回答问题。例如,你不能简单地将package.json转换为package.js。有时候你真的想导入JSON而不是JS。 - curiousdannii
54
根本不是解决方案,你正在导出一个JavaScript对象,它恰好具有与JSON相同的语法。 - Ma Jerez
3
更新问题文本,试图避免进一步的语义争议。 - Gilbert
11
"将其转换为JS" 不是导入JSON文件的解决方案。这样不再是JSON文件了。这是如何导入JSON的 #1 谷歌搜索结果,而主要答案是不要导入JSON。 - Jimbo Jonny
2
.js文件中拥有配置更加方便,因为你可以在其中添加注释。而JSON文件则不支持此功能。 - user1439838
显示剩余3条评论

94

很遗憾,ES6/ES2015不支持通过模块导入语法加载JSON。但是...

有很多方法可以解决这个问题。根据你的需求,你可以查看如何在JavaScript中读取文件(如果你在浏览器中运行,window.FileReader可能是一个选项),或者使用其他加载器,如其他问题中所述(假设你正在使用NodeJS)。

我认为最简单的方法可能是将JSON作为JS对象放入ES6模块并导出它。这样,你可以在需要它的地方直接导入它。

另外值得注意的是,如果你正在使用Webpack,则导入JSON文件默认情况下将工作(因为webpack >= v2.0.0)。

import config from '../config.json';

7
没必要将它放进一个字符串中。毕竟,它被称为 JSON,而不是 JSSN。 - a better oliver
4
同时,torazaburo 在之前被删除的回答中解释道:“并不存在 ES6 的‘模块系统’;只有一个由特定加载器实现的 API。任何加载器都可以做任何它想做的事情,包括支持导入 JSON 文件。例如,一个加载器可能选择支持 import foo from './directory' 的意思是导入 directory/index.js”。 - CodingIntrigue
5
事实上,ES6/ES2015支持通过import语法加载JSON文件:import * as data from './example.json'; - williamli
+1 提醒我们 webpack 会自动处理这个问题。但是要小心,如果你的代码中有 "test: /.js/" 这一行,webpack 将会尝试将你的 JSON 文件编译成 JavaScript,这样就会出现错误。为了解决这个问题,需要将其改为 "test: /.js$/"。 - Rap
webpack 是基于 Node.js 的,不是吗? - Ayyash
显示剩余2条评论

39

如果您正在使用Node,您可以:

const fs = require('fs');

const { config } = JSON.parse(fs.readFileSync('../config.json'));

或者

const evaluation = require('../config.json');
// evaluation will then contain all props, so evaluation.config
// or you could use:
const { config } = require('../config.json');

否则:

// config.js
{
// json object here
}

// script.js

import { config } from '../config.js';

或者

import * from '../config.json'

3
const config = JSON.parse(fs.readFileSync('../config.json')); 对我有用。Node 是一团糟。 - Leif Messinger LOAF
2
我认为这里缺少了参数'utf8',正确的写法应该是: JSON.parse(fs.readFileSync('../package.json', 'utf8')); - Vitaliy Markitanov

30

我正在使用babel+browserify,在目录./i18n/locale-en.json中有一个JSON文件,其中包含一个用于ngTranslate的翻译命名空间。

不需要从JSON文件中导出任何内容(这也是不可能的),我可以使用以下语法对其内容进行默认导入:

import translationsJSON from './i18n/locale-en';

29
感谢所有提出并实现了JSON模块导入断言的人。自Chrome 91以来,您可以直接导入JSON,例如:
// test.json
{
    "hello": "world"
}

// Static Import
import json from "./test.json" assert { type: "json" };
console.log(json.hello);

// Dynamic Import
const { default: json } = await import("./test.json", { assert: { type: "json" } });
console.log(json.hello);

// Dynamic Import
import("./test.json", { assert: { type: "json" } })
  .then(module => console.log(module.default.hello));


注意:其他浏览器目前可能尚未实现此功能。\{\{end\}\}

1
这也被标记为 NodeJS 18 中的实验性功能,并生成 ExperimentalWarning。 - Jeffrey Van Alstine

27

根据您的构建工具和JSON文件中的数据结构,可能需要导入default

import { default as config } from '../config.json';
例如,在Next.js中的使用。

3
TypeScript 中需要添加注释,确保在你的 tsconfig.json 中将 resolveJsonModule 设置为 true - Pat Migliaccio
这是我在 JSON 是数组时唯一有效的解决方案(另一个解决方案——import * as data from './example.json' 只适用于 JSON 是对象的情况)。 - Liran H

26

在现代浏览器中使用fetch:

目前,我们无法导入 JSON MIME 类型的文件,只能导入 JavaScript MIME 类型的文件。这可能会在未来添加功能(官方讨论)。

fetch('./file.json')
  .then(response => response.json())
  .then(obj => console.log(obj))

在 Node.js v13.2+ 版本中:

当前需要使用 --experimental-json-modules 标志,否则默认情况下不支持。

尝试运行

node --input-type module --experimental-json-modules --eval "import obj from './file.json'; console.log(obj)"

并查看将 obj 内容输出到控制台。


我在浏览器中完成了第一个并且它成功了。 - Damilare Koiki

10

有点晚了,但我刚刚遇到了同样的问题,因为我正在尝试为我的 Web 应用提供分析,涉及根据 package.json 版本发送应用程序版本。

配置如下:React + Redux,Webpack 3.5.6

自 Webpack 2+ 以来,json-loader 并没有做太多事情,所以经过一些折腾后,我最终将其删除了。

对我实际起作用的解决方案很简单,就是使用 fetch。 虽然这可能会强制进行一些代码更改以适应异步方法,但它可以完美地工作,特别是考虑到 fetch 会即时提供 json 解码。

所以这就是它:

  fetch('../../package.json')
  .then(resp => resp.json())
  .then((packageJson) => {
    console.log(packageJson.version);
  });

请记住,由于我们在这里特别谈论的是package.json文件,因此该文件通常不会捆绑在您的生产构建中(甚至包括开发环境),因此您将需要使用CopyWebpackPlugin在使用fetch时访问它。


无法。Fetch 不支持本地文件... 你可能正在使用 polyfill 或其他东西。 - sapy
3
几个月之后回复,但是fetch API绝对支持从没有指定服务器主机名的路径加载,以及从相对路径加载。你想的是从file:/// URL加载,而这不是在这里发生的情况。 - Jamie Ridding
1
这是一个非常有帮助的答案,非常简单,并且对我使用本地文件有效。 - Joshua Wilkinson

5

除了其他回答中提到的方法外,在Node.js中,即使在ES模块内部,也可以使用require来读取JSON文件。我发现当读取其他包内的文件时,这种方法尤其有用,因为它利用了Node自己的模块解析策略来定位文件。

在ES模块中,require必须首先通过createRequire创建。

下面是一个完整的示例:

import { createRequire } from 'module';

const require = createRequire(import.meta.url);
const packageJson = require('typescript/package.json');
console.log(`You have TypeScript version ${packageJson.version} installed.`);

在已安装TypeScript的项目中,上述代码将从package.json文件读取并打印TypeScript版本号。

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