JSON.parse()导致错误:`在JSON的位置0处意外的令牌`。

12

我正在编写我的第一个Node.js应用程序。我试图从一个存储在JSON格式中的文件中读取一些数据。

我遇到了以下错误:

SyntaxError: 在JSON的位置0处意外的标记

at Object.parse (native)

这是代码的一部分:

//read saved addresses of all users from a JSON file
fs.readFile('addresses.json', function (err, data) {
    if (data) {
        console.log("Read JSON file: " + data);
        storage = JSON.parse(data);

这里是console.log输出(我检查了json文件本身,它们是相同的):

Read JSON file: {

    "addresses": []

}

那个JSON看起来是正确的。为什么JSON.parse()会失败呢?


2
JSON.parse参数中不支持换行 - MysterX
@MysterX 但是语法错误在位置0吗?JSON.parse()似乎没有启用换行的参数。 - user6269864
1
你需要设置编码,这是因为BOM的原因。 - Netorica
7个回答

28

您的文件开头有一个奇怪的字符。

data.charCodeAt(0) === 65279

我建议:

fs.readFile('addresses.json', function (err, data) {
if (data) {
    console.log("Read JSON file: " + data);
    data = data.trim(); 
    //or data = JSON.parse(JSON.stringify(data.trim()));
    storage = JSON.parse(data);
 }});

我遇到了类似的问题,加上 data.trim() 在运行 JSON.parse() 之前解决了它。 - Valjas
@PrashantTapase trim 不是必需的,但如果您预计 JSON.parse 的输入可能会被 trim 过滤掉的字符破坏,则可以应用它。甚至可以仅在需要时执行: try: json_data = JSON.parse(data); catch(ex) { json_data = JSON.parse(data.trim()); } - bluehipy
为什么需要使用 trim!:) +1,我的问题解决了。谢谢!@bluehipy - Xiaodong Liang
我收到了 TypeError: data.trim is not a function 的错误信息。不过,在将JSON数据复制到TextEdit(MacOS)文件中并以utf-8格式重新保存为file.json文件后,它可以正常工作。 - Rich Steinmetz

10

JSON.parse()不允许有尾随逗号,因此您需要将其删除:

JSON.parse(JSON.stringify(data));

你可以在这里了解更多相关信息。


奇怪,文件中竟然一个逗号都没有? - user6269864
它的意思是: "addresses": [] - Luillyfe
1
这使得我的UTF-16小端文件可以被加载。我的程序不再崩溃了。但是它是纯粹的垃圾。我意识到是我自己造成了这个问题。我使用stringify而不是转换为UTF8。循环中有3600个键值对。这大约是我的文件中字符的数量。 - TamusJRoyce

5

可能是BOM[1]的问题。 我进行了一项测试,将内容为{"name":"test"}的文件保存为UTF-8 + BOM格式,结果出现了同样的错误。

> JSON.parse(fs.readFileSync("a.json"))
SyntaxError: Unexpected token  in JSON at position 0

根据此处的建议 [2],在调用 JSON.parse() 之前,您可以替换或删除它。

例如:

var storage = {};

fs.readFile('a.json', 'utf8', function (err, data) {
    if (data) {
        console.log("Read JSON file: " + data);
        console.log(typeof(data))
        storage = JSON.parse(data.trim());
    }
});

或者

var storage = {};
fs.readFile('a.json', function (err, data) {
    if (data) {
        console.log("Read JSON file: " + data);
        console.log(typeof(data))
        storage = JSON.parse(data.toString().trim());
    }
})

您还可以使用Buffer.slice()删除前三个字节(对于UTF-8)。


1
这在我的每个键中留下了每个字符后面的空格。并且在我的每个字符串值中每个字符后面都有一个空格。 - TamusJRoyce
如果使用另一种编码方式,则BOM的长度就不是3。您可以在维基百科上搜索“字节顺序标记”以查看所有可能的BOM。 - Aria Ax
下面是我展示的UTF-16输出样本。在代码中删除问号图标(Notepad++显示你提到的编码方式)/字节标记并不能解决编码问题。 - TamusJRoyce

4

试着这样做

fs.readFile('addresses.json','utf-8', function (err, data) {
    if (data) {
        console.log("Read JSON file: " + data);
        storage = JSON.parse(data);

这是因为BOM需要在读取文件之前设置编码。这个问题已经在Node.js的Github代码库中发布了。

https://github.com/nodejs/node-v0.x-archive/issues/186


那就是我的问题。只有将文件以UTF-8格式打开并保存,才能解决问题。 - Paulo Henrique

2
为了进一步解释@Luillyfe的答案:
啊哈!fs.readFileSync("data.json")返回一个Javascript对象!
编辑:以下是不正确的...但总结了最初的想法!
我认为那是一个字符串。所以如果文件保存为UTF-8 / ascii,可能不会有问题?从readFileSync返回的javascript对象将转换为JSON.parse可以解析的字符串?不需要调用JSON.stringify吗?
我正在使用powershell保存文件。它似乎将文件保存为UTF-16(现在太忙了,无法检查)。因此,我得到了"SyntaxError:Unexpected token � in JSON at position 0."。
然而,JSON.stringify(fs.readFileSync("data.json"))正确地将返回的文件对象解析为JSON可以解析的字符串。
对我来说的线索是我的json文件内容看起来像下面这样(在将其记录到控制台后):
�{ " R o o m I D _ L o o k u p " :   [
         {
                 " I D " :     1 0 ,
                 " L o c a t i o n " :     " f r o n t " ,
                 " H o u s e " :     " f r o n t r o o m "
         }
}

这似乎不是文件加载到字符串中的内容...

错误示例(不会崩溃...但会将JSON文件转换为无意义的字符!):

const jsonFileContents = JSON.parse(JSON.stringify(fs.readFileSync("data.json")));

我似乎在任何地方都找不到这个。但是很有道理!

编辑:嗯……那个对象只是一个缓冲区。对以上内容表示歉意!

解决方案:

const fs = require("fs");

function GetFileEncodingHeader(filePath) {
    const readStream = fs.openSync(filePath, 'r');
    const bufferSize = 2;
    const buffer = new Buffer(bufferSize);
    let readBytes = 0;

    if (readBytes = fs.readSync(readStream, buffer, 0, bufferSize, 0)) {
        return buffer.slice(0, readBytes).toString("hex");
    }   

    return "";
}

function ReadFileSyncUtf8(filePath) {
    const fileEncoding = GetFileEncodingHeader(filePath);
    let content = null;

    if (fileEncoding === "fffe" || fileEncoding === "utf16le") {
        content = fs.readFileSync(filePath, "ucs2"); // utf-16 Little Endian
    } else if (fileEncoding === "feff" || fileEncoding === "utf16be") {
        content = fs.readFileSync(filePath, "uts2").swap16(); // utf-16 Big Endian
    } else {
        content = fs.readFileSync(filePath, "utf8");
    }

    // trim removes the header...but there may be a better way!
    return content.toString("utf8").trimStart();
}

function GetJson(filePath) {
    const jsonContents = ReadFileSyncUtf8(filePath);
    console.log(GetFileEncodingHeader(filePath));

    return JSON.parse(jsonContents);
}

使用方法:

GetJson("data.json");

注意:目前我不需要这个程序是异步的。如果您可以将其改为异步,请添加另一个答案!

我已将此添加为 Node 的错误:https://github.com/nodejs/node/issues/23033。如果您认为这值得做,请在那里点赞。 - TamusJRoyce
https://nodejs.org/api/util.html#util_textdecoder_decode_input_options 可能具有解决上述问题的功能。 - TamusJRoyce

0
const fs = require('fs');
const myConsole = new console.Console(fs.createWriteStream('./output.json'));
myConsole.log(object);

这将创建一个包含所有输出的输出文件,可以通过console.log(object)触发。

这是将console.log()输出转换为文件的最简单方法。`


0

正如TamusJRoyce所提到的,我最终使用了util.TextDecoder类来实现一种强大的方式来读取UTF-8(无BOM)和UTF-8(带BOM)。假设文件input.json是UTF-8(带或不带BOM)并包含有效的JSON,以下是代码片段。

const fs = require('fs');
const util = require('util');

const rawdata = fs.readFileSync('input.json');
const textDecoder = new util.TextDecoder('utf-8');
const stringData = textDecoder.decode(rawdata);
const objects = JSON.parse(stringData);

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