ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

21 嵌入式操作系统 | JSON 与 json-c:生成与解析

21 嵌入式操作系统 | JSON 与 json-c:生成与解析 嵌入式操作系统 | JSON 与 json-c生成与解析本课程开源地址Giteehttps://gitee.com/fujianxinxi/qianrushixitongyingyongkaifa.git课件、示例代码与验收脚本都在该仓库可直接git clone或下载 ZIP 使用。模块六 · 2 学时 · 环境VMware Ubuntu 20.04一句话概述从上一课 ubus 打出的 JSON 讲起一条命令一步地学 JSON先看懂格式再用 json-c 生成一份多传感器数据写成文件接着解析字符串和文件最后用 Python 的 json 标准库反向校验配套课堂练习与课后作业。本节目标能看懂 JSON 的结构对象、数组、键值对知道它比自定义文本好在哪里会用 json-c 生成 JSON对象/数组/字符串/数字/布尔并写入文件会用 json-c 解析 JSON从字符串、从文件取出字段打印出来一、讲解内容1.1 知识讲解JSON 就是带名字的数据。上一课ubus call打出来的那一段就是 JSON用{}表示对象键值对用[]表示数组值可以是字符串、数字、true/false、还可以再嵌套对象数组。它的好处是人能看懂、机器好解析、跨语言通用C、Python、JavaScript 都支持所以设备上报、前后端交互都用它。本课要生成的数据长这样{device:ubuntu-gateway,uptime_s:1200,online:true,sensors:[{name:温度,value:25.6,unit:C},{name:湿度,value:58.2,unit:%RH}]}json-c 常用接口够用一辈子的那几个函数作用json_object_new_object()/json_object_new_array()建一个对象 / 数组json_object_object_add(root, 键, 值对象)往对象里放一个键值对json_object_array_add(arr, 元素)往数组里追加一个元素json_object_new_string / _int / _double / _boolean造各种类型的值json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY)转成整齐的 JSON 字符串json_tokener_parse(字符串)把 JSON 字符串解析成对象json_object_from_file(文件名)直接从文件读 JSONjson_object_object_get_ex(root, 键, v)从对象里取出某个键的值json_object_get_string / _int / _double / _boolean(v)取值json_object_put(root)释放json-c 是引用计数用完要还注意一个现象浮点数打印得啰嗦。C 程序里写25.6生成的 JSON 里可能是25.600000000000001。这是二进制浮点数的正常现象不是错误Python 用json.load读进来还是 25.6本课最后一步会看到。1.2 操作步骤一条命令一步看现象第 1 步编译-ljson-c是因为用了 json-c 库cd~/lab/课时21-JSON与json-c gcc-Wall-Wextra-ojson_demo json_demo.c -ljson-c现象无输出即成功。json-c 是课时 15 用apt install libjson-c-dev装上的库在系统目录不用自己编。第 2 步先用 Python 造一个 JSON 文件给 C 程序解析终端 Apython3 json_check.pymakecatcmd.json现象已生成 cmd.json { cmd: set, interval: 7, enable: true, target: 127.0.0.1 }第 3 步C 程序解析这个文件终端 B./json_demoreadcmd.json现象③ 解析文件 cmd.json { cmd:set, interval:7, enable:true, target:127.0.0.1 } 取到 interval 7下一步可以拿它当采集间隔第 4 步C 程序生成 JSON 并写文件./json_demo现象① 生成、② 解析内嵌字符串最后提示文件已写① 生成的 JSON整齐格式 { device:ubuntu-gateway, uptime_s:1200, online:true, sensors:[ { name:温度, value:25.600000000000001, unit:C }, { name:湿度, value:58.200000000000003, unit:%RH } ] } 已写入文件report.json ② 解析这段 JSON{cmd:set,interval:7,enable:true} cmd set interval 7 enable true第 5 步用 Python 反向校验 C 生成的 JSON换一种语言读它python3 json_check.py check现象Python 的 json 标准库读进来逐项核对字段校验 report.json [通过] device ubuntu-gateway [通过] uptime_s 1200 [通过] sensors 是数组且有 2 项 [通过] 传感器 温度 的 value 25.6 C [通过] 传感器 湿度 的 value 58.2 %RH 全部通过 ✅注意最后两行C 那边打印的是25.600000000000001Python 读出来是25.6——JSON 的数值本来就是十进制表示这点差别不影响使用。第 6 步自己动手改一改。在build_and_save()里加第三个传感器电量batt或者把device改成自己的学号重新编译运行再用 Python 校验一次。1.3 常见错误与排查现象原因解决gcc: cannot find -ljson-c没装 json-c 开发包sudo apt install -y libjson-c-devjson-c/json.h: No such file or directory同上同上json_tokener_parse返回空JSON 字符串格式不对少引号/逗号把字符串打印出来一个字符一个字符对用json_check.py生成合法 JSON读文件失败读文件失败: cmd.json文件不在当前目录先python3 json_check.py make确认ls -l cmd.json内存一直涨忘了json_object_put()用完的对象要释放引用计数Python 校验报不是合法 JSONC 程序写文件时没写完就退出检查fclose看cat report.json内容是否完整二、学生练习本节命令速查表命令作用示例gcc … -ljson-c编译 json-c 程序gcc -Wall -Wextra -o json_demo json_demo.c -ljson-cpython3 json_check.py make生成 cmd.jsonpython3 json_check.py make./json_demo read 文件解析指定 JSON 文件./json_demo read cmd.json./json_demo生成 report.json./json_demopython3 json_check.py check校验 report.jsonpython3 json_check.py checkcat 文件看 JSON 内容cat report.json编号任务提示预期结果L21.1按步骤跑通双向第 2~5 步Python 生成 → C 解析C 生成 → Python 校验通过L21.2加一个传感器电量仿照温度那三行照抄改值report.json里 sensors 有 3 项校验脚本提示 3 项L21.3把 device 改成自己的学号改字符串Python 校验的 device 项会失败——正好体会校验脚本在查什么L21.4手工把 cmd.json 改坏比如删掉一个逗号再./json_demo read cmd.json看到解析失败/字段读不到L21.5用jshn看 JSONecho ‘{“a”:1}’jshn 之类试一下L21.6用 Python 生成更复杂的 JSON加一个嵌套对象C 程序能用json_object_object_get_ex取到三、作业必做完成 L21.1、L21.2提交三张截图./json_demo输出 /cat report.json/ Python 校验通过并回答① JSON 里对象和数组分别怎么写② 为什么 C 打印的浮点数和 Python 读到的看起来不一样③ 为什么要json_object_put选做把配置做成 JSON 文件写一个 C 程序读cmd.json里的interval然后按这个间隔在屏幕上打印计数时间间隔就用课时 18 的 uloop 定时器提交截图 文字回答命名课时21-作业截图.png四、本节小结与下节预告本节学会了 JSON 的生成与解析对象数组怎么造、怎么写成文件、怎么从字符串和文件里读回来并用 Python 做了交叉校验。下节进入模块六的最后一课——串口虚拟机里没有真串口我们用 socat 造一对虚拟串口用 Modbus RTU 协议把数据读回来CRC16 正好复用课时 10 的代码。附录怎么运行 完整代码一个课件就够用先按附录 A 的顺序敲命令服务端/对端先开自己的程序后开再按附录 B 抄代码。行尾请用 LF。附录 A怎么运行注意先后顺序gcc-Wall-Wextra-ojson_demo json_demo.c -ljson-c python3 json_check.pymake# ① 生成 cmd.json./json_demoreadcmd.json# ② C 解析这个文件./json_demo# ③ C 生成 report.json 解析内嵌 JSONpython3 json_check.py check# ④ Python 校验 report.json附录 B文件json_demo.c/* * json_demo.c —— 课时 21用 json-c 生成和解析 JSON * * 编译gcc -Wall -Wextra -o json_demo json_demo.c -ljson-c * 运行./json_demo ① 生成 JSON打印 存成 report.json * ② 解析一段固定 JSON打印字段 * ./json_demo read 文件名 只解析指定的 JSON 文件 */#includestdio.h#includestdlib.h#includestring.h#includejson-c/json.h/* ① 用 json-c 造一份多传感器上报数据打印出来并存成文件 */staticvoidbuild_and_save(constchar*filename){structjson_object*root,*arr,*item;FILE*fp;rootjson_object_new_object();json_object_object_add(root,device,json_object_new_string(ubuntu-gateway));json_object_object_add(root,uptime_s,json_object_new_int(1200));json_object_object_add(root,online,json_object_new_boolean(1));arrjson_object_new_array();/* 数组多个传感器 */itemjson_object_new_object();json_object_object_add(item,name,json_object_new_string(温度));json_object_object_add(item,value,json_object_new_double(25.6));json_object_object_add(item,unit,json_object_new_string(C));json_object_array_add(arr,item);itemjson_object_new_object();json_object_object_add(item,name,json_object_new_string(湿度));json_object_object_add(item,value,json_object_new_double(58.2));json_object_object_add(item,unit,json_object_new_string(%RH));json_object_array_add(arr,item);json_object_object_add(root,sensors,arr);printf(① 生成的 JSON整齐格式\n%s\n,json_object_to_json_string_ext(root,JSON_C_TO_STRING_PRETTY));fpfopen(filename,w);if(fp){fprintf(fp,%s\n,json_object_to_json_string_ext(root,JSON_C_TO_STRING_PRETTY));fclose(fp);printf( 已写入文件%s\n,filename);}else{perror(fopen);}json_object_put(root);/* 释放json-c 是引用计数 */}/* ② 解析一段 JSON 文本取出里面的字段 */staticvoidparse_text(constchar*text){structjson_object*root,*v;printf(\n② 解析这段 JSON%s\n,text);rootjson_tokener_parse(text);if(!root){printf( 解析失败格式不对\n);return;}if(json_object_object_get_ex(root,cmd,v))printf( cmd %s\n,json_object_get_string(v));if(json_object_object_get_ex(root,interval,v))printf( interval %d\n,json_object_get_int(v));if(json_object_object_get_ex(root,enable,v))printf( enable %s\n,json_object_get_boolean(v)?true:false);json_object_put(root);}/* ③ 从文件读 JSON 并解析 */staticvoidparse_file(constchar*filename){structjson_object*root,*v;rootjson_object_from_file(filename);if(!root){printf(读文件失败%s先用 ./json_check.py make 生成它\n,filename);return;}printf(③ 解析文件 %s\n%s\n,filename,json_object_to_json_string_ext(root,JSON_C_TO_STRING_PRETTY));if(json_object_object_get_ex(root,interval,v))printf( 取到 interval %d下一步可以拿它当采集间隔\n,json_object_get_int(v));json_object_put(root);}intmain(intargc,char*argv[]){if(argc3strcmp(argv[1],read)0){parse_file(argv[2]);return0;}build_and_save(report.json);parse_text({\cmd\:\set\,\interval\:7,\enable\:true});printf(\n完成报告写在 report.json可用 python3 json_check.py check 校验\n);return0;}附录 C文件json_check.py#!/usr/bin/env python3课时 21 的测试工具用 Python 的标准库 json 生成/校验 JSON 文件 用法 python3 json_check.py make # 生成 cmd.json给 C 程序解析用 python3 json_check.py check # 校验 C 程序生成的 report.json importjsonimportsys MODEsys.argv[1]iflen(sys.argv)1elsecheckifMODEmake:cmd{cmd:set,interval:7,enable:True,target:127.0.0.1}withopen(cmd.json,w,encodingutf-8)asf:json.dump(cmd,f,ensure_asciiFalse,indent2)print(已生成 cmd.json)print(json.dumps(cmd,ensure_asciiFalse,indent2))print(接下来执行 ./json_demo read cmd.json)sys.exit(0)# check校验 C 程序生成的 report.jsontry:withopen(report.json,encodingutf-8)asf:datajson.load(f)exceptFileNotFoundError:print(找不到 report.json先运行 ./json_demo 生成它)sys.exit(2)exceptjson.JSONDecodeErrorase:print(freport.json 不是合法 JSON{e})sys.exit(1)okTruedefcheck(cond,msg):globalokprint(( [通过] ifcondelse [失败] )msg)ifnotcond:okFalseprint(校验 report.json)check(data.get(device)ubuntu-gateway,fdevice {data.get(device)})check(isinstance(data.get(uptime_s),int),fuptime_s {data.get(uptime_s)})sensorsdata.get(sensors)check(isinstance(sensors,list)andlen(sensors)2,fsensors 是数组且有{len(sensors)ifisinstance(sensors,list)else?}项)ifisinstance(sensors,list)andsensors:forsinsensors:check(isinstance(s.get(value),(int,float)),f传感器{s.get(name)}的 value {s.get(value)}{s.get(unit)})print(全部通过 ✅ifokelse有失败项 ❌)sys.exit(0ifokelse1)
返回列表