TL;DR InfluxDB技术提示:处理JSON对象和数组映射

导航到

有多种方法可以使用Flux从各种不同的来源导入数据,包括SQL数据库、其他InfluxDB Cloud账户、来自URL的标注CSV和JSON。然而,之前您只能像这个示例中描述的那样手动使用Flux从JSON对象构建表。

我们将描述如何使用三个具有越来越复杂JSON类型的示例。首先,我们将使用元语言示例描述如何使用这些JSON类型。然后,我们将通过使用OpenWeatherMap API的实例来讨论现实生活中的例子。

在使用Flux处理JSON对象时,以下函数和注意事项非常重要

  1. display()函数:使用此函数将任何值的Flux表示形式作为字符串返回。这允许您在InfluxDB UI中可视化您的JSON对象。此功能在最新的开源版本中也已发布,请确保您的软件是最新的。
  2. array.from()函数:使用此函数与display()函数一起在InfluxDB UI中以字符串形式查看JSON对象。我们将在以下部分讨论如何一起使用这两个函数。
  3. json.parse() 函数:使用此函数将 JSON 对象转换为 Flux 值。有关类型转换,请参阅这里。在使用 http.post() 进行 API 调用时,您必须将此函数应用于 JSON 对象响应。
  4. time() 函数:使用此函数将字符串时间戳转换为时间值。
  5. array.map() 函数:使用此函数遍历数组以处理复杂的 JSON 类型。

An-example-of-parsing-a-JSON-in-the-Data-Explorer-in-the-InfluxDB-UI

InfluxDB UI 中的 数据探索器 中解析 JSON 的示例

JSON 示例 1

第一个示例:从键中提取顶层值。

  • 将以下 JSON 转换为
    JSON = {"a": 1, 
     "b": 2, 
     "c": 3,
      "time": timestamp_1}
  • 以下 Flux 表格
    time a b c
    timestamp_1 1 2 3
  • 以下 Flux 代码
    jsonData = json.parse(data: JSON)
    // uncomment the next line to display the JSON
    // array.from(rows: [{data: display(v: jsonData)}])
    
    // convert the timestamp to a time type with the time() function
    // use the array.from() function to create a table 
    array.from(rows: [{_time: time(v: int(v: jsonData.timestamp_1)) , a: jsonData.a, b: jsonData.b, c: jsonData.c }])
  • 在此处找到运行此示例的代码 这里

JSON 示例 2

第二个示例:在具有复杂类型的数组上映射。

  • 将以下 JSON 转换为
    JSON = {"a": 1,
     "list": [{"b": 1.1, 
                "time": timestamp_1},
               {"b": 1.2, 
                "time": timestamp_2}
              ]
    }
  • 以下 Flux 表格
    time a b
    timestamp_1 1 1.1
    timestamp_2 1 1.2
  • 以下 Flux 代码
    jsonData = json.parse(data: JSON)
    // uncomment the next line to display the JSON
    // array.from(rows: [{data: display(v: jsonData)}])
    
    // extract the list that we want to map across with array.map 
    listData = jsonData.list
    // gather any top level values that you want to include in your table as constants
    a = jsonData.a
    // map across each complex type in the array named "list" 
    list = array.map(
    arr: listData,
    fn: (x) => ({
    // convert the timestamp to a time type with the time() function
    "_time": time(v::x.time),
    "b": x.b,
    "city": a
    })
    )
    // finally convert that flattened list into a table with array.from 
    array.from(rows: list)
  • 在此处找到运行此示例的代码 这里

JSON 示例 3

第三个示例:在包含复杂类型的嵌套数组上映射。

  • 将以下 JSON 转换为
    JSON = {"list": [{"b": 1.1, 
                "nestedList": [{"alpha": "foo1", "beta": "bar1"}, {"alpha": "foo2", "beta": "foo2"}],
                "time": 1596632400000000000},
               {"b": 1.2, 
     	       "nestedList": [{"alpha": "foo1", "beta": "bar1"}, {"alpha": "foo2", "beta": "foo2"}],
                "time": 1596632500000000000}
              ]
    }
  • 以下 Flux 表格
    time alpha b
    timestamp_1 foo1 1.1
    timestamp_2 foo1 1.2
  • 以下 Flux 代码
    jsonData = json.parse(data: JSON)
    
    // uncomment the next line to display the JSON
    // array.from(rows: [{data: display(v: jsonData)}])
    
    // extract the list that we want to map across with array.map 
    listData = jsonData.list
    // map across each complex type in the array named "list" 
    list = listData |> array.map(fn:(x) => {
                 pendingList = x.nestedList
                 return {
                     id: x.b,
                     _time: time(v: int(v: x.time)),
                     alpha: pendingList[0].alpha,
                 }
             })
    array.from(rows: list)
  • 在此处找到运行此示例的代码 这里

JSON 示例 4

第四个示例:在包含嵌套数组的复杂类型上映射,并评估值是否存在。此示例与示例 3 类似,但它可以用于处理可能包含 null 值的实例。

  • 将以下 JSON 转换为
    JSON = {"list": [{"b": 1.1, 
                "nestedList": [{"alpha": "foo1", "beta": "bar1"}, {"alpha": "foo2", "beta": "foo2"}],
                "time": 1596632400000000000},
               {"b": 1.2, 
     	       "nestedList": [{"alpha": "null", "beta": "bar1"}, {"alpha": "foo2", "beta": "foo2"}],
                "time": 1596632500000000000}
              ]
    }
  • 以下 Flux 表格
    time alpha b
    timestamp_1 foo1 1.1
    timestamp_2 foo1 1.2
  • 以下 Flux 代码
    jsonData = json.parse(data: JSON)
    
    // uncomment the next line to display the JSON
    // array.from(rows: [{data: display(v: jsonData)}])
    
    // extract the list that we want to map across with array.map 
    listData = jsonData.list
    // map across each complex type in the array named "list" 
    list = listData |> array.map(fn:(x) => {
                 pendingList = x.nestedList
                 // filter for the value 
                     |> array.filter(fn: (x) => x.alpha == "foo1")
                 // handle null values
                 pendingAlpha = if length(arr: pendingList) == 1
                     then
                         pendingList[0].alpha
                     else
                         "foo1"
                 return {
                     id: x.b,
                     _time: time(v: int(v: x.time)),
                     alpha: pendingAlpha,
                 }
             })
    array.from(rows: list)
  • 在此处找到运行此示例的代码 这里

OpenWeatherMap API 示例:复杂类型的数组

在此示例中,我们将了解如何映射包含嵌套 JSON 对象的数组以构建表格,这使得在 Flux 中处理 JSON 更加容易。最后,我们将使用 Flux 将表格写入 InfluxDB,步骤如下

  1. 使用 http.get() 函数提交 GET 请求到 API 并返回 JSON 对象。
  2. 使用 json.encode() 函数将对象转换为字节。
  3. 使用 json.parse() 函数将 JSON 对象或数组转换为 Flux 记录或数组。
  4. 使用 array.map() 函数映射数组中的元素。
  5. 使用 array.from() 函数构建表格。
  6. 使用 to() 函数将数据写入 InfluxDB。

在本节中,我们将使用 OpenWeatherMap 当前天气数据 API 使用 小时预测端点 收集过去 4 天内伦敦的每小时天气数据。我们返回的 JSON 响应有 96 个时间戳,如下所示

{
  "cod": "200",
  "message": 0.0179,
  "cnt": 96,
  "list": [
   {
     "dt": 1596632400,
     "main": {
       "temp": 289.16,
       "feels_like": 288.41,
       "temp_min": 289.16,
       "temp_max": 289.16,
       "pressure": 1013,
       "sea_level": 1013,
       "grnd_level": 1010,
       "humidity": 78,
       "temp_kf": 0
     },
     "weather": [
       {
         "id": 804,
         "main": "Clouds",
         "description": "overcast clouds",
         "icon": "04n"
       }
     ],
     "clouds": {
       "all": 100
     },
     "wind": {
       "speed": 2.03,
       "deg": 252,
       "gust":5.46
     },
     "visibility": 10000,
     "pop": 0.04,
     "sys": {
       "pod": "n"
     },
     "dt_txt": "2020-08-05 13:00:00"
   },
   .....
       ],
  "city": {
   "id": 2643743,
   "name": "London",
   "coord": {
     "lat": 51.5085,
     "lon": -0.1258
   },
   "country": "GB",
   "timezone": 0,
   "sunrise": 1568958164,
   "sunset": 1569002733
  }
}

以下 Flux 代码

  1. 导入必要的库
  2. 将请求对象存储在变量 resp 中
  3. 解析响应的 JSON 主体并将列表属性存储在变量 nestedListData 中。此属性包含包含伦敦时间戳和温度的数组列表。
  4. 映射对象列表以提取日期和时间。温度作为主属性中的嵌套数组存在。Unix 日期时间值通过乘以 1000000000 转换为纳秒精度。使用 time() 函数将 Unix 时间戳转换为 RFC3339 格式。此时间戳转换执行是为了更好地可视化数据。
  5. 使用array.from()函数构建表格,并将提取的时间、温度值列表传入。
  6. 使用set()函数创建名为_weather和_field的列,分别对应值为"weather"和"temp"。这是为使用需要您的表具有以下列的to()函数做准备:
    • _time
    • _measurement
    • _field
    • _value
  7. 使用to()函数将数据写入名为"bucket"的桶。
import "experimental/json"
import "experimental/http"
import "experimental/array"

weatherURL = "https://pro.openweathermap.org/data/2.5/forecast/hourly?lat=35&lon=139&appid=<yourAPIkey>
// http.get() returns the response from the API 
resp = http.get(url: weatherURL)
// json.parse function returns a json object with lists, records, strings, booleans, and floats
jsonData = json.parse(data: resp.body)
// get the list object 
listData = jsonData.list
// extract the city name just once
city = jsonData.city.name
list = array.map(
arr: listData,
fn: (x) => ({
// convert the timestamp to a time type with the time() function
"_time": time(v::x.dt*1000000000),
"_value": x.main.temp,
"city": city
})
)

array.from(rows: list)
|> set(key: "_field", value: "temp")
|> set(key: "_measurement", value: "weather")

|> yield()
|> to(bucket: "bucket")

关于使用Flux处理JSON对象的最终想法

希望这篇InfluxDB技术技巧帖子能激发您利用Flux来请求和操作JSON响应。现在您可以使用array.map()函数映射JSON中的嵌套数组来构建行。像往常一样,您可以使用array.from()函数从这些行中构建表格。在准备输出以符合to()函数的数据要求后,我们最终能够写入表格。这种功能为将数据导入InfluxDB提供了另一种方式。您可以在任务中按计划执行此逻辑,定期从HTTP请求中写入数据。

如果您使用Flux并需要帮助,请在我们的社区网站Slack频道中寻求帮助。如果您正在InfluxDB之上开发酷炫的IoT应用,我们非常乐意了解,请确保分享您的故事!另外,请分享您的想法、担忧或问题在评论部分。我们非常乐意收到您的反馈并帮助您解决遇到的问题!