在 Node.js 中将 CSV 数据转换为 JSON

不久前,我编写了一项服务,该服务从 Yahoo! Finance 获取历史数据,以便根据证券的表现生成蒙特卡罗模拟。对于那些可能不熟悉蒙特卡罗模拟的人来说,这是一种概率建模技术,用于评估涉及随机变量的复杂场景中的潜在结果。通过模拟多个概率场景,蒙特卡罗模拟有助于分析投资、商业、物理和工程等各个领域的风险和不确定性。

Yahoo! Finance 的一大优点是它允许您以 CSV 格式下载历史数据。唯一的问题是我的服务(与许多服务一样)以 JSON 格式提供响应。这需要对传入数据进行转换。本文将详细介绍我如何使用出色的 Node.js csvtojson 库在 Node.js 中实现这一点。

比较 CSV 和 JSON 格式

让我们快速浏览一下这两种格式,并确定从一种格式转换为另一种格式需要做什么。

CSV(逗号分隔值)是一种用于存储表格数据的简单文件格式。在 CSV 文件中,每一行代表一行数据,每行中的值用逗号分隔。它是将数据导入数据库的最流行格式之一。例如,以下是 Yahoo! Finance 响应的示例:

Date,Open,High,Low,Close,Adj Close,Volume
2004-08-19,2.490664,2.591785,2.390042,2.499133,2.499133,897427216
2004-08-20,2.515820,2.716817,2.503118,2.697639,2.697639,458857488
2004-08-23,2.758411,2.826406,2.716070,2.724787,2.724787,366857939
2004-08-24,2.770615,2.779581,2.579581,2.611960,2.611960,306396159
2004-08-25,2.614201,2.689918,2.587302,2.640104,2.640104,184645512
2004-08-26,2.613952,2.688672,2.606729,2.687676,2.687676,142572401
2004-08-27,2.692408,2.705360,2.632383,2.643840,2.643840,124826132

同时,JSON(JavaScript 对象表示法)是一种轻量级的基于文本的数据交换格式,人类可以轻松读取和编写,机器也可以轻松解析和生成。它由两个主要结构组成:对象(用花括号 {} 括起来的键值对)和数组(用方括号 [] 括起来的有序列表)。为了让您了解它是什么样子,以下是我的 API 服务的部分响应:

{
  "stockData": [
    {
      "Date": "2019-01-02T00:00:00.000Z",
      "Close": 52.233059
    },
    {
      "Date": "2019-01-03T00:00:00.000Z",
      "Close": 50.745255
    },
    {
      "Date": "2019-01-04T00:00:00.000Z",
      "Close": 53.474648
    },
    {
      "Date": "2019-01-07T00:00:00.000Z",
      "Close": 53.35878
    },
    {
      "Date": "2019-01-08T00:00:00.000Z",
      "Close": 53.752831
    },
    // etc...
  ]
}

csvtojson 入门

有几个类似的库,其中 csvtojson 是最活跃的库之一。要使用 Node 包管理器 (npm) 安装它,请在终端中运行以下命令:

npm i csvtojson

接下来,将以下导入语句添加到 Node 脚本的顶部:

const csvtojson = require("csvtojson");

就这样,我们就可以使用该库了!

转换数据

为了转换数据,我们需要做的就是使用几个选项调用 csvtojson 构造函数,然后将其链接到 fromString() 方法:

try {
    const response = await fetch(url);
    if (response.status !== 200) {
      throw new Error('Fetch failed. Received a response of '
                     + response.status);
    }
    const data = await csvtojson({
      checkType: true,
      colParser: { 
        "Date": dt => new Date(dt),
        "Open": "omit",
        "High": "omit",
        "Low": "omit",
        "Volume": "omit"
      }
    }).fromString(await response.text());

    return data;
  } catch (err) {
    // handle errors
  }

还有一个 fromStream() 方法,但我发现通过分离调用可以更容易地处理与 fetch 相关的错误。

关于选项的一些信息:

  • 默认情况下,csvtojson 将每个值设置为字符串。要覆盖此行为,我们可以将 checkType 属性设置为 true。这样做会告诉 csvtojson 尝试根据单元格值找到合适的类型解析器。也就是说,如果单元格值为“5”,则将使用 numberParser,并且该列下的所有值都将使用 numberParser 来转换数据。有用于字符串和数字的内置解析器,但没有用于日期的解析器。这就是“日期”列采用自定义转换方法的原因。
  • 还有另外一个内置解析器:“omit”值表示省略整列。
  • 我们可以使用点访问器符号来访问我们转换后的数据,即 `data.Date` 或 `data['Adj Close']`。

    更改属性名称

    API 响应对象中的“Close”属性实际上是“Adj Close”,但我不想这么叫,所以我改了属性名。为了转换发送给下游的结果,我们可以对每个 json 对象(即每个转换后的行)使用 .subscribe() 方法。

    为此,我将“Adj Close”值分配给新属性,然后删除现有列。由于转换过程的异步性质,这一切都在 Promise 中完成:

    const data = await csvtojson({
          checkType:true,
          colParser: { 
            "Date": dt => new Date(dt),
            "Open": "omit",
            "High": "omit",
            "Low": "omit",
            "Volume": "omit"
          }
        }).fromString(await response.text()).subscribe(jsonObj => 
          new Promise(resolve => {
            // use the adj close
            jsonObj.Close = jsonObj['Adj Close'];
            delete jsonObj['Adj Close'];
            resolve();
          })
        );

    结论

    在本教程中,我们学习了如何转换 Yahoo! 财经的证券历史价格信息,使用 Node.js csvtojson 库从 CVS 转换为 JSON。

    您可以在 RapidAPI 上试用 Monte Carlo 模拟器 API。我还编写了一个 API,用于获取股票买入价以获得期望的回报和一个信用卡总利息计算器。所有这些都可以免费使用,直到达到一定门槛,然后需要付费订阅。

    如果您对我的 API 有任何疑问,或者想要咨询有关构建自定义 API 的信息,请随时给我发送电子邮件:raylsstr(AT)gmail(DOT)com。