- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
导读
本文围绕 ThingsBoard 集成(Integration)数据转换器中解码器(Decoder)函数的 JSON 输出格式展开,核心示例取自仓库中的 simple_json_output.md。你将掌握解码器返回对象的完整字段契约(deviceName/deviceType/attributes/telemetry)、带时间戳(ts)的时序数据写法、deviceLabel/customerName/groupName等扩展字段的用法,以及如何返回 JSON 数组实现一条消息批量创建设备或资产;同时结合后端解析源码,理解这些字段在被 AbstractUplinkDataConverter 解析时的校验规则与默认值行为,从而写出可稳定落库、可调试排错的解码器脚本。
一、解码器输出格式在数据转换链路中的位置
在 ThingsBoard 的 Integration 模块中,上行数据(Uplink)从外部设备进入平台后,需要经过数据转换器(Converter)中的解码器(Decoder)脚本,把原始 payload(可能是二进制、CSV、HEX 或 JSON)翻译成平台统一的数据模型。解码器脚本的执行入口位于 ScriptUplinkDataConverter:
String decoderField = ScriptLanguage.JS.equals(scriptInvokeService.getLanguage()) ? "decoder" : "tbelDecoder"; String decoder = configuration.getConfiguration().get(decoderField).asText();即解码器支持 JavaScript 与 TBEL 两种语言,分别读取配置中的decoder与tbelDecoder字段。脚本执行的原始结果是一段字符串,随后在 AbstractUplinkDataConverter#convert 中被JsonParser.parseString(rawResult)解析为JsonElement:
JsonElement element = JsonParser.parseString(rawResult); List<UplinkData> resultList = new ArrayList<>(); if (element.isJsonArray()) { for (JsonElement uplinkJson : element.getAsJsonArray()) { resultList.add(parseUplinkData(uplinkJson.getAsJsonObject(), finalMetadata)); } } else if (element.isJsonObject()) { resultList.add(parseUplinkData(element.getAsJsonObject(), finalMetadata)); }这段源码揭示了两条关键事实:
- 解码器返回的字符串必须是合法的 JSON,且必须是一个 JSON 对象或 JSON 数组;
- 对象会被逐条解析,数组中的每个元素视为一条独立的 UplinkData,因此数组可以用于一次转换多条设备/资产数据。
提示:以上调试与解析逻辑还支持 Debug 模式下将转换前后的原始数据与结果持久化(见同文件中的
persistUplinkDebug调用),便于排查解码器输出是否正确。
二、基础输出结构:最简单的 JSON 对象
仓库中的 simple_json_output.md 给出了解码器输出对象的最小骨架:
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "attributes": { "serialNumber": "SN-111" }, "telemetry": { "temperature": 42, "humidity": 80 } }这个对象由四个顶层字段组成:
| 字段 | 是否必填 | 说明 |
|---|---|---|
deviceName | 必填(设备场景) | 平台中设备的名称,若不存在会被自动创建 |
deviceType | 可选 | 设备类型;缺省时后端使用默认类型DEFAULT_DEVICE_TYPE |
attributes | 可选 | 客户端属性(Client-Side Attributes)键值对,会写入平台属性存储 |
telemetry | 可选 | 时序遥测数据键值对,作为当前时刻的遥测写入 |
上述解析逻辑与 AbstractUplinkDataConverter#parseUplinkData 完全对应。例如设备名与类型的读取:
entityName = src.get("deviceName").getAsString(); builder.deviceName(entityName); if (src.has("deviceType")) { builder.deviceType(src.get("deviceType").getAsString()); } else { builder.deviceType(DEFAULT_DEVICE_TYPE); }而telemetry与attributes则分别经由parseTelemetry、parseAttributesUpdate转换为平台内部的PostTelemetryMsg与PostAttributeMsg(底层基于 Transport 层 Protobuf 消息)。也就是说,解码器返回 JSON 后,平台会自动完成从 JSON 到内部传输协议的转换,开发者无需关心序列化细节。
2.1 关键校验规则
从 getIsAssetAndVerify 的实现可以看到两条硬性校验,解码器脚本必须遵守:
deviceName与assetName至少出现其一,否则抛出JsonParseException("Either 'deviceName' or 'assetName' should be present in the converter output!");- 二者不能同时出现,否则抛出
JsonParseException("Both 'deviceName' and 'assetName' can't be present in the converter output!"); - 若选择资产场景(只提供
assetName),则必须同时提供assetType,否则抛出JsonParseException("Asset type is not set!")。
因此在编写解码器时,务必根据原始数据只填充设备字段或资产字段之一,避免输出对象同时携带两个名称字段。
三、带时间戳的遥测输出:ts + values 结构
基础示例中的telemetry是一个扁平键值对象,表示"当前时刻"的遥测。若原始数据携带了时间信息,则需要使用 simple_json_output_with_ts.md 展示的ts + values结构:
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "attributes": { "serialNumber": "SN-111" }, "telemetry": { "ts": 1527863043000, "values": { "temperature": 42, "humidity": 80 } } }此时telemetry对象包含两个字段:
ts:Unix 毫秒时间戳(epoch milliseconds),指示这条遥测数据对应的时间点;values:实际的遥测键值对。
在 simple-json 完整示例 中可以看到典型的解码器写法——把人类可读的时间字符串换算成 epoch 毫秒:
// decode payload to JSON. See helper function below var json = decodeToJson(payload); // convert date to epoch in milliseconds var timestamp = Date.parse(json.ts); // Construct result object with time-series data var result = { deviceName: json.serialNumber, deviceType: "Thermostat", deviceLabel: "Kitchen Thermostat", telemetry: { ts: timestamp, values: { temperature: json.t, humidity: json.h } } };配合仓库中的 payload.md(输入{"serialNumber": "SN-111", "ts": "2021-11-21 14:27:39 UTC", "t": 36.6, "h": 70})与 output.md(输出ts: 1637504859000),可以看到一条完整的"原始 payload → 解码函数 → 标准输出"链路。需要说明的是:这里Date.parse是否支持非标准日期格式取决于运行环境(JavaScript 执行器),生产环境中建议先对时间字符串做规范化解析后再转换。
四、扩展字段:deviceLabel、customerName、groupName、integrationName
除了最小骨架,解码器输出还支持若干可选扩展字段,用于在转换阶段直接完成实体的业务归属配置。见 label_json_output.md 与 json_output.md:
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "deviceLabel": "Room A thermostat", "customerName": "Company Name", "groupName": "Thermostats", "attributes": { "model": "Model A", "serialNumber": "SN-111", "integrationName": "Test integration" }, "telemetry": { "temperature": 42, "humidity": 80 } }各扩展字段的语义如下:
| 字段 | 说明 |
|---|---|
deviceLabel | 设备的显示标签,与deviceName分离,更友好地展示设备 |
customerName | 该设备所属客户(Customer)的名称,平台会按名称查找或创建客户并完成归属 |
groupName | 设备分组名称,转换后设备会被加入指定分组 |
attributes.integrationName | 属性中的integrationName仅是一个普通属性键示例,并非特殊字段;integrationName这类业务属性同样通过attributes写入平台 |
对应后端解析(见上文parseUplinkData):
if (src.has("deviceLabel")) { builder.deviceLabel(src.get("deviceLabel").getAsString()); } if (src.has("customerName")) { builder.customerName(src.get("customerName").getAsString()); } if (src.has("groupName")) { builder.groupName(src.get("groupName").getAsString()); }从源码可见,deviceLabel仅在设备场景生效(位于isAsset为 false 的分支中),而customerName与groupName对设备和资产均适用。这些字段的完整语义(如 customerName 是按名称自动查找/创建还是必须已存在)取决于转换器的后续 Uplink 处理逻辑,编写脚本时建议在测试环境中先验证实际行为。
五、JSON 数组输出:一次上报多设备/资产
当一条外部消息包含多条设备数据(例如网关批量转发、多传感器聚合报文)时,解码器可以返回 JSON 数组。仓库中的 json_array_output.md 给出了同时包含设备与资产两种实体的完整示例:
[ { "deviceName": "001B638446E7", "deviceType": "thermostat", "deviceLabel": "Room A thermostat", "attributes": { "model": "Model A" }, "telemetry": [ { "ts": 1527863043000, "values": { "battery": 3.99, "temperature": 27.05 } }, { "ts": 1527863044000, "values": { "battery": 3.98, "temperature": 27.06 } } ] }, { "assetName": "OF-123", "assetType": "office", "attributes": { "model": "Model A" }, "telemetry": { "ts": 1527863041000, "values": { "battery": 3.99, "temperature": 27.05 } } } ]这个示例同时展示了三种进阶能力:
- 数组逐条解析:数组中的每个对象被独立解析成一条
UplinkData(对应前文element.isJsonArray()分支); - 单实体多时间点遥测:设备
001B638446E7的telemetry本身是数组,携带了两个不同ts的时间点,实现一条转换结果回填多段历史时序数据; - 资产混合输出:第二个对象改用
assetName+assetType(必填),平台据此创建/更新名为OF-123、类型为office的资产。混合输出时要注意同一对象内deviceName与assetName不能并存(见 2.1 节校验规则)。
类似的数组输出同样出现在 complex-json-hex/output.md 中,其values内还嵌入了rawData对象,说明遥测值除了标量(数字、字符串、布尔)外,也支持嵌套 JSON 对象——平台会把嵌套对象按 JSON 类型键值(JSON_V)写入时序数据。这与 filterKeyValueAndUpdateMap 中对JSON_V类型的显式支持相印证。
六、编写解码器输出时的实践建议
综合仓库中的示例文档与后端解析源码,可以总结出以下可直接落地的经验:
- 统一使用标准 JSON 返回:解码器
return的对象会被JSON.stringify成字符串后再被后端JsonParser解析,务必保证输出是合法的 JSON 对象或数组(例如使用JSON.parse而非手工拼串,参考 decoder_fn.md 中decodeToJson的写法)。 - 名称字段的完备性:每个输出对象要么包含
deviceName(可选deviceType、deviceLabel),要么包含assetName+assetType;两者互斥。 - 时间字段统一毫秒:需要回填历史遥测时使用
telemetry.ts(epoch 毫秒)+telemetry.values;不带ts则数据落为当前时刻。 - 合理利用数组:网关批量上报场景返回数组,每个元素对应一个实体;单实体多时间点可把
telemetry写成ts/values对象数组。 - 利用 Debug 模式验证:转换器支持 Debug 输出,可将转换前后的原始 payload 与 JSON 结果持久化查看,用于核对字段是否被正确解析。
七、相关资源索引
- 输出格式官方示例(本文主文档):simple_json_output.md
- 带时间戳输出:simple_json_output_with_ts.md
- 扩展字段(label/customer/group):label_json_output.md、json_output.md
- 数组输出:json_array_output.md
- 完整链路示例(payload + 解码函数 + 输出):simple-json 目录、complex-json-hex 目录
- 后端解析实现:AbstractUplinkDataConverter、ScriptUplinkDataConverter
掌握了解码器 JSON 输出契约,你就能在 ThingsBoard 的各类集成(MQTT、HTTP、CoAP、TCP 等)中编写出结构正确、语义清晰的数据转换脚本,让任意原始格式的报文都能稳定地映射为平台统一的设备、属性与时序数据模型。
- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
相关推荐
ThingsBoard 上行转换器解码器 JSON 数组输出指南:一次上报多设备与多条遥测数据
ThingsBoard 上行转换器解码器 JSON 数组输出指南:一次上报多设备与多条遥测数据 导读 ThingsBoard 的 Integration(集成)
物联网后端数据可视化消息队列ThingsBoard 数据转换器 Decoder 输出格式详解:simple-json 示例与源码级剖析
ThingsBoard 数据转换器 Decoder 输出格式详解:simple json 示例与源码级剖析 本文以 ThingsBoard 开源 IoT 平台内
物联网后端数据可视化消息队列ThingsBoard 上行数据解码器(Decoder)JSON 输出格式全解:从 deviceName 到 telemetry 的标准结果契约
ThingsBoard 上行数据解码器(Decoder)JSON 输出格式全解:从 deviceName 到 telemetry 的标准结果契约 导读 在 Th
物联网后端数据可视化消息队列
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考