问题
当被 decode 的 JSON 对象中存在重复 key 时,对该对象做任意修改(触发物化)后再 encode,输出会包含字面上的重复 key,且重复 key 都携带被收敛后的同一个值(last-wins)。
这一行为由 #60(有序 encode)引入。
复现
local qjson = require("qjson")
local t = qjson.decode('{"a":1,"a":2,"b":3}')
t.b = 30
print(qjson.encode(t))
| 场景 |
输出 |
| 实际(本分支,修改后) |
{"a":2,"a":2,"b":30} |
| 期望 |
{"a":2,"b":30} |
| 未修改(fast-path 切片原始字节) |
{"a":1,"a":2,"b":3}(原样保留) |
main 分支修改后 |
{"b":30,"a":2}(去重,但丢失顺序) |
根因
lua/qjson/table.lua 的 LazyObject.__newindex 物化循环(约 :336-350)把每个 cursor 条目都追加进 keys 列表(包含重复项),而 values[key] 会被后值覆盖。encode_lazy_object_walking 的物化分支(约 :551-563)随后遍历 keys 逐个输出,于是重复 key 各输出一次,且都带着收敛后的同一个值。
建议修复
物化时仅在 key 首次出现时才追加到 keys:
local key = ffi.string(strp_box[0], size_box[0])
if values[key] == nil then -- 仅首次出现时记录顺序
keys[#keys + 1] = key
end
local cached = cached_child(t, key)
if cached ~= nil and not INTERNAL_KEYS[key] then
values[key] = cached
else
values[key] = decode_cursor(t, child_box)
end
这样 last-wins 值仍然生效,但只保留单个 key,与 lua-cjson 的 decode 语义一致。(注意 JSON null 解码为 _M.null 哨兵 table 而非 nil,因此 values[key] == nil 判定不会被空值/null 值误伤。)
备注
- 重复 key 属于 RFC 8259 未定义 / 实现自定行为,优先级低。
- 存在一个相关的细微读取不一致:
t.a 在修改前返回 1(首值,qjson_cursor_field 返回第一个匹配),物化后返回 2(末值)。修复物化去重不改变这一读取行为;是否需要统一「首值 / 末值」语义可在本 issue 下一并讨论。
- 建议同时把
tests/lua/ordered_encode_spec.lua 中嵌套场景的 out:find(...) 断言加强为精确字符串匹配,以覆盖此类重复/顺序问题。
问题
当被 decode 的 JSON 对象中存在重复 key 时,对该对象做任意修改(触发物化)后再 encode,输出会包含字面上的重复 key,且重复 key 都携带被收敛后的同一个值(last-wins)。
这一行为由 #60(有序 encode)引入。
复现
{"a":2,"a":2,"b":30}{"a":2,"b":30}{"a":1,"a":2,"b":3}(原样保留)main分支修改后{"b":30,"a":2}(去重,但丢失顺序)根因
lua/qjson/table.lua的LazyObject.__newindex物化循环(约:336-350)把每个 cursor 条目都追加进keys列表(包含重复项),而values[key]会被后值覆盖。encode_lazy_object_walking的物化分支(约:551-563)随后遍历keys逐个输出,于是重复 key 各输出一次,且都带着收敛后的同一个值。建议修复
物化时仅在 key 首次出现时才追加到
keys:这样 last-wins 值仍然生效,但只保留单个 key,与 lua-cjson 的 decode 语义一致。(注意 JSON
null解码为_M.null哨兵 table 而非nil,因此values[key] == nil判定不会被空值/null值误伤。)备注
t.a在修改前返回1(首值,qjson_cursor_field返回第一个匹配),物化后返回2(末值)。修复物化去重不改变这一读取行为;是否需要统一「首值 / 末值」语义可在本 issue 下一并讨论。tests/lua/ordered_encode_spec.lua中嵌套场景的out:find(...)断言加强为精确字符串匹配,以覆盖此类重复/顺序问题。