4. JSON指令格式:数据结构、封装规范与字段命名

在物联网设备远程控制场景中,JSON(JavaScript Object Notation)凭借其轻量、自描述、跨语言的特点,已成为指令封装的事实标准。本章将系统讲解JSON数据结构、指令封装规范、字段命名规则以及嵌套对象与数组的使用方法,确保你能够设计出既符合规范又易于调试的指令格式。

4.1 JSON基础数据结构

JSON支持六种基本数据类型,在指令封装中各有典型用途:

数据类型 示例 指令场景
字符串(String) "device_id": "sensor_01" 设备标识、指令名称、状态描述
数字(Number) "temperature": 25.6 传感器数值、定时时长、阈值
布尔(Boolean) "power": true 开关状态、使能标志
空值(Null) "error": null 无错误、未初始化字段
对象(Object) "config": { "mode": "auto" } 参数分组、嵌套配置
数组(Array) "targets": [1, 2, 3] 批量操作、多点控制
实践要点:在嵌入式设备端解析JSON时,优先使用数字类型而非字符串表示数值,避免在MCU上进行额外的类型转换,减少内存开销。

4.2 指令封装规范

一个标准的RemoteCommand指令JSON应包含以下核心字段,形成统一的指令信封:

{
  "cmd_id": "CMD_20250321_001",
  "type": "control",
  "timestamp": 1711000000,
  "source": "cloud_server",
  "target": {
    "device_id": "sensor_01",
    "group": "temperature_sensors"
  },
  "action": "set_config",
  "params": {
    "sampling_interval": 10,
    "threshold": 30.0,
    "alarm_enabled": true
  },
  "ttl": 60,
  "priority": 2
}

字段说明:

  • cmd_id:全局唯一指令ID,用于去重和追踪
  • type:指令类型(control/query/config/ota)
  • timestamp:Unix时间戳,用于时序校验
  • source:指令发起方标识
  • target:目标设备对象,支持单设备或设备组
  • action:具体操作名称
  • params:操作参数对象
  • ttl:指令生存时间(秒),超时丢弃
  • priority:优先级(0-5,数值越大优先级越高)
避坑指南:不要将指令参数直接平铺在顶层字段中。始终使用params对象封装操作参数,这样便于扩展和版本兼容。例如,新增参数时只需在params中添加字段,不会破坏顶层结构。

4.3 字段命名规则

统一的命名规则能显著提升指令的可读性和跨团队协作效率。推荐采用以下规范:

规则项 推荐做法 反例
命名风格 snake_case(小写+下划线) camelCase / PascalCase / kebab-case
字段长度 2-4个单词,不超过30字符 very_long_and_descriptive_field_name
语义明确 device_id / sampling_interval d_id / si / val1
布尔字段 is_enabled / has_error / use_ssl enabled / error / ssl
时间字段 timestamp / expire_at / created_at time / t / date
枚举值 UPPER_SNAKE_CASE lowercase / mixedCase
团队约定:建议在项目初期制定JSON字段命名规范文档,并通过lint工具(如ajv、jsonschema)进行自动化校验,避免因命名不一致导致的解析错误。

4.4 嵌套对象与数组

实际物联网场景中,指令往往需要表达复杂的层级关系。嵌套对象和数组是实现这一目标的核心手段。

4.4.1 嵌套对象

嵌套对象用于将相关参数组织在一起,形成清晰的层级结构:

{
  "cmd_id": "CMD_20250321_002",
  "action": "set_control_params",
  "params": {
    "pid": {
      "kp": 1.5,
      "ki": 0.02,
      "kd": 0.1,
      "output_limit": {
        "min": 0,
        "max": 100
      }
    },
    "sensor_config": {
      "type": "temperature",
      "filter": {
        "method": "moving_average",
        "window_size": 5
      }
    }
  }
}

嵌套深度建议不超过3层,过深的嵌套会增加解析复杂度和内存占用,尤其在资源受限的嵌入式设备上。

4.4.2 数组的使用

数组用于表达批量操作、多点控制或时序数据:

{
  "cmd_id": "CMD_20250321_003",
  "action": "batch_set",
  "params": {
    "devices": [
      {
        "id": "sensor_01",
        "target_temp": 25.0
      },
      {
        "id": "sensor_02",
        "target_temp": 26.5
      },
      {
        "id": "sensor_03",
        "target_temp": 24.0
      }
    ],
    "schedule": ["08:00", "12:00", "18:00"],
    "priority_list": [2, 1, 3]
  }
}
性能提示:在嵌入式设备端解析数组时,建议预先分配最大容量,避免动态内存分配带来的碎片和不确定性。例如,使用静态数组+长度计数器的模式。

4.5 JSON指令结构总览

下图展示了RemoteCommand指令的完整JSON结构,包含指令信封、目标设备、操作参数以及嵌套对象和数组的典型用法:

RemoteCommand JSON指令结构图 指令根对象 cmd_id type timestamp source priority target (Object) action params (Object) ttl device_id group sampling_interval threshold alarm_enabled devices (Array) schedule (Array) 元素 0 元素 1 元素 2 嵌套深度建议 对象嵌套 ≤ 3层 数组元素 ≤ 32个(嵌入式限制)

4.6 指令校验与调试

在设备端解析JSON指令前,建议进行以下校验:

  1. 结构校验:检查必填字段是否存在,类型是否正确
  2. 范围校验:数值字段是否在合理范围内(如温度-40~85℃)
  3. 嵌套深度校验:防止恶意构造的深层嵌套导致栈溢出
  4. 数组长度校验:确保不超过设备端预分配缓冲区大小
// 嵌入式C语言校验示例(伪代码)
typedef struct {
    char cmd_id[32];
    int  type;
    long timestamp;
    char source[16];
    int  priority;
    // 嵌套对象
    Target target;
    Params params;
    int  ttl;
} RemoteCommand;

int validate_json_command(const char* json_str) {
    // 1. 解析JSON
    cJSON* root = cJSON_Parse(json_str);
    if (root == NULL) return -1;
    
    // 2. 校验必填字段
    if (!cJSON_HasObjectItem(root, "cmd_id")) return -2;
    if (!cJSON_HasObjectItem(root, "action")) return -3;
    
    // 3. 校验嵌套深度(递归检查)
    if (get_nested_depth(root) > MAX_NEST_DEPTH) return -4;
    
    // 4. 校验数组长度
    cJSON* devices = cJSON_GetObjectItem(root, "devices");
    if (devices && cJSON_IsArray(devices)) {
        int len = cJSON_GetArraySize(devices);
        if (len > MAX_DEVICE_COUNT) return -5;
    }
    
    cJSON_Delete(root);
    return 0; // 校验通过
}
调试技巧:在开发阶段,建议在设备端串口输出JSON解析的详细日志,包括每个字段的解析结果和耗时。使用cJSON_Print()可以格式化输出解析后的JSON树,便于对比原始指令。

4.7 常见错误与规避

错误类型 错误示例 正确做法
字段名拼写不一致 deviceId vs device_id 统一使用snake_case
类型不匹配 "priority": "2"(字符串) "priority": 2(数字)
嵌套过深 对象嵌套超过5层 控制在3层以内
数组元素类型不一致 [1, "two", true] 保持元素类型一致
缺少默认值 可选字段未提供时解析失败 设备端设定合理的默认值

通过遵循上述JSON指令格式规范,你可以构建出结构清晰、易于扩展、便于调试的远程控制指令体系,为后续的设备端响应处理奠定坚实基础。