跳转至

json - json 生成和解析库

作者:马梦阳

一、概述

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,因其结构清晰、易于阅读和编写,被广泛应用于配置文件、网络通信和数据存储等场景。在嵌入式开发中,高效、可靠的 JSON 解析与生成功能对于设备与云端或其他系统之间的数据交互至关重要。

为此,LuatOS 提供了 json 核心库,无需额外依赖即可快速实现 JSON 数据的解析(反序列化)与生成(序列化)。

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/json;

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "json_demo"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- json库支持将 Lua 对象 转为 JSON 字符串, 或者反过来, JSON 字符串 转 Lua 对象
-- 若转换失败, 会返回nil值, 强烈建议在使用时添加额外的判断
function main_task()
    while 1 do
        sys.wait(1000)
        -- 序列化成功示例:
        -- 示例一:Lua string 转为 JSON string;
        local data = "test"
        local json_str, err_msg = json.encode(data)
        log.info("string_string_test1", json_str, err_msg)
        -- 日志输出:"test" nil

        -- 示例二:Lua number 转为 JSON string;
        local data = 123456789
        local json_str, err_msg = json.encode(data)
        log.info("number_string_test1", json_str, err_msg)
        -- 日志输出:"123456789" nil

        -- 示例三:Lua boolean 转为 JSON string;
        local data = true
        local json_str, err_msg = json.encode(data)
        log.info("boolean_string_test1", json_str, err_msg)
        -- 日志输出:"true" nil

        -- 示例四:Lua table 转为 JSON string;
        local data = {abc = 123, def = "123", ttt = true}
        local json_str, err_msg = json.encode(data)
        log.info("table_string_test1", json_str, err_msg)
        -- 日志输出:"{"abc":123,"ttt":true,"def":"123"}" nil

        -- 示例五:Lua nil 转为 JSON string;
        local data = nil
        local json_str, err_msg = json.encode(data)
        log.info("nil_string_test1", json_str, err_msg)
        -- 日志输出:"nil" nil

        -- 序列化失败示例:
        -- Lua table 中包含 function;
        local data = {abc = 123, def = "123", ttt = true, err = function() end}
        local json_str, err_msg = json.encode(data)
        log.info("table_string_test2", json_str, err_msg)
        -- 日志输出:"nil Cannot serialise function: type not supported"

        -- 指定浮点数示例:
        -- 指定保留三位小数,不足时补零,超出时四舍五入;
        local data = {abc = 1234.56789}
        local json_str, err_msg = json.encode(data, "3f")
        log.info("table_string_test3", json_str, err_msg)
        -- 日志输出:"{"abc":1234.568}" nil

        -- 反序列化成功示例:
        -- 示例一:JSON string 转为 Lua string
        local str = '"test"'
        -- local str = "\"test\""
        local obj, result, err = json.decode(str)
        log.info("string_string_test1", obj, result, err)
        -- 日志输出:"test" 1 nil

        -- 示例二:JSON string 转为 Lua number;
        local str = "123456789"
        local obj, result, err = json.decode(str)
        log.info("string_number_test1", obj, result, err)
        -- 日志输出:123456789 1 nil

        -- 示例三:JSON string 转为 Lua boolean;
        local str = "true"
        local obj, result, err = json.decode(str)
        log.info("string_boolean_test1", obj, result, err)
        -- 日志输出:true 1 nil

        -- 示例四:JSON string 转为 Lua table;
        local str = "{\"abc\":1234545}"
        local obj, result, err = json.decode(str)
        log.info("string_table_test1.1", obj, result, err)
        -- 日志输出:table: 01C9A490 1 nil
        log.info("string_table_test1.2", obj.abc, result, err)
        -- 日志输出:1234545 1 nil

        -- 反序列化失败示例:
        -- JSON string 不是合法的 JSON 格式;
        local str = "{\"def\":}"
        local obj, result, err = json.decode(str)
        log.info("string_table_test2", obj, result, err)
        -- 日志输出:"nil false Expected value but found T_OBJ_END at character 8"

        -- empty table(空表) 转换为 JSON 时的说明:
        -- 原生 Lua 中的 table 是数组(sequence)和哈希表(map)的统一数据结构;
        -- 空表 {} 在转换为 JSON 时存在歧义:无法确定应输出为空数组 [] 还是空对象 {};
        -- 由于 Lua 中只有包含连续正整数索引(从 1 开始)的表才被视为数组,而空表不满足这一条件;
        -- 因此 JSON 库默认将其序列化为 {}(空对象);
        local data = {abc = {}}
        local json_str, err_msg = json.encode(data)
        log.info("table_string_test3", json_str, err_msg)
        -- 日志输出:{"abc":{}} nil

        -- 字符串中包含控制字符(如 \r\n)的 JSON 序列化与反序列化说明:
        -- 在 Lua 中,字符串可以包含任意字符,包括回车(\r)、换行(\n)等控制字符;
        -- 当使用 json.encode() 对包含此类字符的字符串进行序列化时;
        -- JSON 库会自动将其转义为标准的 JSON 字符串字面量形式(例如 \r 转为 "\r",\n 转为 "\n");
        -- 以确保生成的 JSON 符合规范且可安全传输;
        -- 反序列化时(json.decode()),这些转义序列会被正确还原为原始的控制字符;
        -- 因此解码后的字符串与原始字符串在内容上完全一致(逐字节相等);
        -- 需要注意的是:日志输出函数(如 log.info)在打印包含 \r\n 的字符串时;
        -- 会实际执行回车换行操作,导致日志在终端上分行显示;
        -- 这可能造成“输出格式混乱”或“多出缩进/空格”的视觉错觉(例如 true 前有一个空格长度);
        -- 但这只是日志显示效果,并非字符串内容本身发生变化;
        -- 实际比较(tmp3.str == tmp)结果为 true,证明序列化与反序列化过程是无损且正确的;
        local tmp = "ABC\r\nDEF\r\n"
        local tmp2 = json.encode({str=tmp})
        log.info("json", tmp2)
        -- 日志输出:{"str":"ABC\r\nDEF\r\n"}
        local tmp3 = json.decode(tmp2)
        log.info("json", "tmp3", tmp3.str, tmp3.str == tmp)
        -- 日志输出:tmp3 ABC
        --          DEF
        --           true
        -- 注:true前存在一个空格长度,这是日志输出格式导致的,与字符串内容本身无实际差异。

        -- json.null 的语义与比较行为说明:
        -- 在标准 JSON 中,null 是一个合法的字面量,表示“空值”或“无值”;
        -- 然而,Lua 语言本身没有 null 类型,只有 nil 用于表示未定义或空值;
        -- 为在 Lua 中准确表示 JSON 的 null,json 库提供了一个特殊占位符:json.null;
        -- json.null 通常是一个轻量级的 userdata 或 table,具体实现依赖库版本;
        -- 当使用 json.encode() 序列化包含 json.null 的字段时,该字段会被正确转换为 JSON 中的 null;
        -- 反之,json.decode() 在解析 JSON 中的 null 时,会将其还原为 json.null,而非 Lua 的 nil;
        -- 这是因为:若将 JSON 的 null 直接转为 nil,会导致 table 中对应键被删除;
        -- 从而丢失原始 JSON 的结构信息;
        -- 因此,json.decode('{"abc":null}').abc 的结果是 json.null,而不是 nil;
        -- 由于 json.null 是一个具体的值(非 nil),它与 nil 比较结果为 false;
        -- 只有与 json.null 自身比较时,结果才为 true;
        -- 开发者应始终使用 == json.null 来判断某个字段是否为 JSON 的 null;
        -- 而不要用 == nil,否则逻辑将出错;
        log.info("json.null", json.encode({name=json.null}))
        -- 日志输出:{"name":null}
        log.info("json.null", json.decode("{\"abc\":null}").abc == json.null)
        -- 日志输出:true
        log.info("json.null", json.decode("{\"abc\":null}").abc == nil)
        -- 日志输出:false
    end
end

-- 启动主任务
sys.taskInit(main_task)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

每个常量对应的常量取值仅做日志打印时查询使用,不要将这个常量取值用做具体的业务逻辑判断,因为LuatOS内核固件可能会变更每个常量对应的常量取值;

如果用做具体的业务逻辑判断,一旦常量取值发生改变,业务逻辑就会出错;

json 库没有常量;

四、函数详解

4.1 json.encode(obj,t)

功能

将 Lua 对象序列化为符合 JSON 格式的字符串;

注意事项

1. obj 参数仅支持字符串、数字、布尔值、表、nil 类型作为输入,不支持函数、用户数据、线程和非空轻量级用户数据;

2. t 参数从 2024.1.9 版本起默认使用 "%.7f" 格式,之前默认使用 "%.7g" 格式;

参数

obj

参数含义:需要序列化的 Lua 对象;
数据类型:string/number/boolean/table/nil
取值范围:受内存分配限制,内存不够时会报错;
是否必选:必须传入此参数;
注意事项:仅支持字符串、数字、布尔值、表、nil 类型;
         不支持函数、用户数据、线程和非空轻量级用户数据;
         table 类型时最大嵌套深度为 1000,超过限制会抛出 "excessive nesting" 错误;
参数示例:-- 添加 Lua string 对象;
         obj = "test"

         -- 添加 Lua number 对象;
         obj = 123456789

         -- 添加 Lua boolean 对象;
         obj = true

         -- 添加 Lua table 对象;
         obj = {abc = 123, def = "123", ttt = true}

         -- 添加 Lua nil 对象;
         obj = nil

t

参数含义:浮点数格式控制字符串,用于指定序列化时浮点数的输出格式;
         LuatOS 仅支持 f  g 两种格式;
         f 格式是以十进制小数形式输出浮点数,不使用科学计数法;
         g 格式是自动在 f  e(科学计数法) 之间选择更紧凑的表示方式;
数据类型:string
取值范围:1 < 字符串长度 < 8
是否必选:可选传入此参数(默认为 "%.7f");
注意事项:若未提供此参数,默认使用 "%.7f"
         长度限制必须满足 1 < 字符串长度 < 8
         结尾字符必须以 'f'  'g' 结尾;
         数字部分只能是 0-14 之间的整数;
         f 格式中数字部分表示小数点后位数,g 格式中数字部分表示有效数字总位数;
         支持带百分号的完整格式(如 "%12f")或者不带百分号的部分格式(如 "12f");
         %.f  %.g 在不填数字部分时固定值为 6
参数示例:-- 使用 %.12f 的方式转换为 json 字符串;
         t = "12f"

返回值

local json_str, err_msg = json.encode(obj, t)

有两个返回值 json_str、err_msg;

json_str

含义说明:成功时返回 UTF-8 编码格式的 JSON 字符串;
数值类型:string
取值范围:无特别限制;
注意事项:仅当 obj 为合法类型且内容可序列化时返回;否则返回 nil
返回示例:-- Lua string 对象序列化成功后的返回值;
         "test"

         -- Lua number 对象序列化成功后的返回值;
         "123456789"

         -- Lua boolean 对象序列化成功后的返回值;
         "true"

         -- Lua table 对象序列化成功后的返回值;
         "{"abc":123,"ttt":true,"def":"123"}"

         -- Lua nil 对象序列化成功后的返回值;
         "nil"

         -- 序列化失败后的返回值;
         nil

err_msg

含义说明:序列化失败时的错误信息;
数值类型:string
取值范围:无特别限制;
注意事项:序列化成功时返回值为 nil
         序列化失败时返回值为 错误信息;
返回示例:-- 其中一种错误信息;
         "Cannot serialise function: type not supported"

示例

-- 序列化成功示例:
-- 示例一:Lua string 转为 JSON string;
local data = "test"
local json_str, err_msg = json.encode(data)
log.info("string_string_test1", json_str, err_msg)
-- 日志输出:"test" nil

-- 示例二:Lua number 转为 JSON string;
local data = 123456789
local json_str, err_msg = json.encode(data)
log.info("number_string_test1", json_str, err_msg)
-- 日志输出:"123456789" nil

-- 示例三:Lua boolean 转为 JSON string;
local data = true
local json_str, err_msg = json.encode(data)
log.info("boolean_string_test1", json_str, err_msg)
-- 日志输出:"true" nil

-- 示例四:Lua table 转为 JSON string;
local data = {abc = 123, def = "123", ttt = true}
local json_str, err_msg = json.encode(data)
log.info("table_string_test1", json_str, err_msg)
-- 日志输出:"{"abc":123,"ttt":true,"def":"123"}" nil

-- 示例五:Lua nil 转为 JSON string;
local data = nil
local json_str, err_msg = json.encode(data)
log.info("nil_string_test1", json_str, err_msg)
-- 日志输出:"nil" nil

-- 序列化失败示例:
-- table 中包含 function;
local data = {abc = 123, def = "123", ttt = true, err = function() end}
local json_str, err_msg = json.encode(data)
log.info("table_string_test2", json_str, err_msg)
-- 日志输出:"nil Cannot serialise function: type not supported"


-- 指定浮点数示例:
-- 指定保留三位小数,不足时补零,超出时四舍五入;
local data = {abc = 1234.56789}
local json_str, err_msg = json.encode(data, "3f")
log.info("table_string_test3", json_str, err_msg)
-- 日志输出:"{"abc":1234.568}" nil

4.2 json.decode(str)

功能

将符合 JSON 格式的字符串反序列化为 Lua 对象;

注意事项

1. 输入字符串必须是合法的 JSON 文本,否则会解析失败;

2. 成功时返回反序列化结果和状态码 1,失败时返回 nil、状态码 0 和错误信息;

参数

str

参数含义:需要反序列化的 JSON 字符串;
数据类型:string
取值范围:受内存分配限制,内存不够时会报错;
是否必选:必须传入此参数;
注意事项:必须是 UTF-8 编码格式的 JSON 字符串;
         JSON 嵌套层级最大为 1000,超过会触发错误;
         字符串长度受系统可用内存限制,当内存不足时会返回错误;
参数示例:-- 添加正确的 json string,此时反序列化成功后的值为 Lua string 类型;
         str = '"test"'
         str = "\"test\""

         -- 添加正确的 json string,此时反序列化成功后的值为 Lua number 类型;
         str = "123456789"

         -- 添加正确的 json string,此时反序列化成功后的值为 Lua boolean 类型;
         str = "true"

         -- 添加正确的 json string,此时反序列化成功后的值为 Lua table 类型;
         str = "{\"abc\":1234545}"

         -- 添加非法的 json string;
         str = "{\"def\":}"

返回值

local obj, result, err = json.decode(str)

有三个返回值 obj、result、err;

obj

含义说明:反序列化成功后得到的 Lua 对象;
数值类型:string/number/boolean/table
取值范围:无特别限制;
注意事项:反序列化成功时为 Lua 对象;
         反序列化失败时为 nil
返回示例:-- 反序列化成功后的 Lua string 返回值;
         "test"

         -- 反序列化成功后的 Lua number 返回值;
         123456789

         -- 反序列化成功后的 Lua boolean 返回值;
         true

         -- 反序列化成功后的 Lua table 返回值;
         table: 01C9A490

         -- 反序列化失败后的返回值;
         nil

result

含义说明:操作结果状态码;
数值类型:number
取值范围:1  0
注意事项:无;
返回示例:-- 反序列化成功后的返回值:
         1

         -- 反序列化失败后的返回值:
         0

err

含义说明:反序列化失败时的错误信息;
数值类型:string
取值范围:无特别限制;
注意事项:反序列化成功时返回值为 nil
         反序列化失败时返回值为 错误信息;
返回示例:-- 其中一种错误信息;
         "Expected value but found T_OBJ_END at character 8"

示例

-- 反序列化成功示例:
-- 示例一:JSON string 转为 Lua string
local str = '"test"'
-- local str = "\"test\""
local obj, result, err = json.decode(str)
log.info("string_string_test1", obj, result, err)
-- 日志输出:"test" 1 nil

-- 示例二:JSON string 转为 Lua number;
local str = "123456789"
local obj, result, err = json.decode(str)
log.info("string_number_test1", obj, result, err)
-- 日志输出:123456789 1 nil

-- 示例三:JSON string 转为 Lua boolean;
local str = "true"
local obj, result, err = json.decode(str)
log.info("string_boolean_test1", obj, result, err)
-- 日志输出:true 1 nil

-- 示例四:JSON string 转为 Lua table;
local str = "{\"abc\":1234545}"
local obj, result, err = json.decode(str)
log.info("string_table_test1.1", obj, result, err)
-- 日志输出:table: 01C9A490 1 nil
log.info("string_table_test1.2", obj.abc, result, err)
-- 日志输出:1234545 1 nil


-- 反序列化失败示例:
-- 字符串不是合法的 JSON 格式;
local str = "{\"def\":}"
local obj, result, err = json.decode(str)
log.info("string_table_test2", obj, result, err)
-- 日志输出:"nil false Expected value but found T_OBJ_END at character 8"

五、产品支持说明

支持 LuatOS 开发的所有产品都支持 json 核心库。