在使用JSON.parse解析有效的JSON时,在节点中出现“在JSON中的位置0处发现意外标记”。

8

我已经为这个问题苦恼了几个小时了。

我有一个简单的Node服务器,正在调用外部API,以获取(巨大的,大约4+ MB)JSON数据。我使用的请求模板是从Node文档中直接取出的,非常基础:

const muniURL = `http://api.511.org/transit/vehiclemonitoring?api_key=${API_KEYS.API_KEY_511}&format=json&agency=sf-muni`;

http.get(muniURL, (res) => {
  const statusCode = res.statusCode;
  const contentType = res.headers['content-type'];
  console.log('Status Code:', statusCode);
  console.log('Content Type:', contentType);

  let error;
  if (statusCode !== 200) {
    error = new Error(`Request Failed.\n` +
                      `Status Code: ${statusCode}`);
  } else if (!/^application\/json/.test(contentType)) {
    error = new Error(`Invalid content-type.\n` +
                      `Expected application/json but received ${contentType}`);
  }
  if (error) {
    console.log(`Request error: ${error.message}`);
    // consume response data to free up memory
    res.resume();
    return;
  }

  res.setEncoding('utf8');
  let rawData = '';
  res.on('data', (chunk) => rawData += chunk);
  res.on('end', () => {
    try {
      const parsedData = JSON.parse(rawData);
      console.log('parsedData:', parsedData);
    } catch (e) {
      console.log(`Caught error: ${e.message}`);
    }
  });
}).on('error', (e) => {
  console.log(`Got error: ${e.message}`);
});

...每次,它都会命中catch语句,并显示:Caught error: Unexpected token in JSON at position 0。(请注意'token'和'in'之间有两个空格。)

我已经使用两个不同的基于Web的JSON验证器检查了Chrome和Postman返回的JSON,并且它被认为是有效的。而将rawData写入文件时,看起来像是一个缓冲区(?)...

1fef bfbd 0800 0000 0000 0400 efbf bdef
bfbd efbf bd72 efbf bdc8 b62d efbf bd2b
0c3f 7547 1cef bfbd 00ef bfbd efbf bd0b
efbf bd5b 49ef bfbd 2def bfbd 6c6b efbf
bd5c 55ef bfbd efbf bd44 3fef bfbd 126c
71ef bfbd 021c 2029 6def bfbd 13ef bfbd
efbf bdef bfbd 437f 52ef bfbd 4227 48ef
bfbd efbf bd4d efbf bd31 13ef bfbd 09ef
bfbd 5d2f 7bef bfbd efbf bde5 aa81 745e
efbf bd65 efbf bd31 efbf bdef bfbd efbf
...

在这里Buffer.isBuffer返回false。

到目前为止,我尝试了首先JSON.stringifytoString,转换为new Buffer然后再序列化,去除空格.trim以及替换各种转义字符,但都没有成功。

我错过了什么吗?


编辑:我意识到我正在验证由Chrome和Postman获取的JSON,它们显然正在进行某种预处理。 curl URL会产生一堆肯定不是JSON的混乱。 我依然不知道那一堆混乱实际上是什么数据类型,以及为什么明确请求JSON时却没有得到JSON。


将 rawData 写入文件时的 1fef 字符是反引号字符 (`) 的 utf8 代码,这让我想到 JSON.parse() 可能会出错。 - Rocky Sims
如果在这一行代码之前使用 console.log(rawData);,它会输出什么内容? const parsedData = JSON.parse(rawData); - Rocky Sims
1
@RockySims 在那里记录的是一堆乱码,绝对不是 JSON:��ε�@+���@}<��ot�Xu���y��֙�%���"���٤��r�$�����ҙ��@2�+LDSX�ѥ~r���k�����fg�( ~J,�GS����T��$�'�����nc$XfI%1t!FO@)�>�ܪ��Z�Z��7q30�uV�衷�����ZrR3��4�>���*J�� ��<��͏����2o�&m�R�:�|p��>�[=��x}m��{���1�����E�������4�O�Q��+�rjZ�ȏ@��}��ź6ҪG�u��[�ڞ"�妕d|t��>�}+|z��>j\k�O�l��IY�d��Ӓ=0�vYTe]iޝ#SQb�9-l���6}U��[����?d���y&我认为是 Chrome/Postman 进行了一些自动预处理,导致我被误导了。使用 curl 命令访问该 URL 也会得到同样的非 JSON 消息。但我不知道我正在获取什么数据类型。 - dangerismycat
1个回答

12

看起来api.511.org正在对任何提供有效api_key的api调用应用gzip。此外,它在JSON响应中返回了一个无效的第一个字符。

以下是解决方法:

var request = require('request');

var apiUrl = 'http://api.511.org/transit/vehiclemonitoring?api_key=${API_KEYS.API_KEY_511}&format=json&agency=sf-muni';
//apiUrl = 'http://ip.jsontest.com/';

var response = request({
    method: 'GET',
    uri: apiUrl,
    gzip: true
}, function(error, response, body) {
    //* workaround for issue with this particular apiUrl
    var firstChar = body.substring(0, 1);
    var firstCharCode = body.charCodeAt(0);
    if (firstCharCode == 65279) {
        console.log('First character "' + firstChar + '" (character code: ' + firstCharCode + ') is invalid so removing it.');
        body = body.substring(1);
    }
    //*/

    var parsedJson = JSON.parse(body);
    console.log('parsedJson: ', parsedJson);
});

你是救命恩人!!非常感谢你!! - dangerismycat
很高兴能帮到你。哦,还请确保切换回使用您自己的API密钥。在发布答案之前,我忘记删除我的测试API密钥了。 - Rocky Sims
没问题,我只是调整了你的答案并继续使用我的API密钥。再次感谢! - dangerismycat
我很喜欢Stack Overflow,我刚刚调试了一个小时。 - Gireesh
非常感谢您提供的解决方案。在我的情况下,我已经将一个Excel文件转换为JSON文件,然后通过一个为Node.js编写的程序读取JSON文件。 - Deepak
我很高兴听到它对你有所帮助。谢谢你的评论让我知道了。 - Rocky Sims

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