DCS:World Lua脚本JSON编码遇unexpected type `function`错误的排查
DCS World Lua脚本JSON编码"unexpected type
function"问题排查与解决 问题背景
为DCS:World编写的Lua脚本,用于提取任务数据中的目标信息并发送至Node.js服务器。其他功能正常,但执行local jsonString = self.JSON:encode(message).." \n"时抛出**"unexpected type function"**错误。测试type(message)返回table,但JSON序列化仍失败,需定位根源、排查隐藏的函数类型值并找到最优解决办法。
相关代码
koUDPSocket = {} koUDPSocket.host = "127.0.0.1" --koTCPSocket.host = "85.221.224.254" koUDPSocket.port = 52525 koUDPSocket.socket = socket.udp() koUDPSocket.JSON = require "JSON" function koUDPSocket:send(data, typee) local message = { typee = typee, data = data, } env.info("SENDING:") env.info(koEngine.TableSerialization(message)) local jsonString = self.JSON:encode(message).." \n" socket.try(self.socket:sendto(jsonString, self.host, self.port)) env.info("UDP DATA SENT") end function countUnitsInTable(table) local count = 0 for _, _ in pairs(table) do count = count + 1 end return count end function koEngine.loadMissionDataShort() koEngine.debugText("koEngine.loadMissionDataShort") koEngine.debugText("loading shorted mission data") for categoryName, categoryTable in pairs(MissionData) do if type(categoryTable) == "table" and categoryName ~= "properties" then for objectiveName, objectiveTable in pairs(categoryTable) do if type(objectiveTable) == "table" then local unitCount = 0 if objectiveTable.groups then for _, group in pairs(objectiveTable.groups) do if group.units then unitCount = unitCount + countUnitsInTable(group.units) end end end local shortData = { underAttack = objectiveTable.underAttack, coa = objectiveTable.coa, status = objectiveTable.status, priority = objectiveTable.priority, type = categoryName, units = unitCount } missionDataShort[objectiveName] = shortData koEngine.debugText("insert: " .. tostring(objectiveName)) end end end end koUDPSocket:send(missionDataShort, "MissionDataShort") koEngine.debugText("END populating short data") end
日志片段
2023-08-23 19:27:44.840 INFO SCRIPTING (Main): SENDING: 2023-08-23 19:27:44.848 INFO SCRIPTING (Main): { ['data'] = { ['FARP Oche'] = { ['type'] = 'FARP', ['underAttack'] = false, ['status'] = 'closed', ['priority'] = 'strategic', ['units'] = 0, ['coa'] = 'neutral', }, ['Antenna Bulls'] = { ['type'] = 'Communication', ['underAttack'] = false, ['status'] = 'closed', ['priority'] = 'strategic', ['units'] = 0, ['coa'] = 'neutral', }, ['FARP Tsvirmi'] = { ['type'] = 'FARP', ['underAttack'] = false, ['status'] = 'closed', ['priority'] = 'strategic', ['units'] = 0, ['coa'] = 'neutral', }, ['Sukhumi-Babushara'] = { ['type'] = 'Aerodrome', ['underAttack'] = false, ['status'] = 'open', ['priority'] = 'strategic', ['units'] = 27, ['coa'] = 'blue', }, ['FARP Lentehi'] = { ['type'] = 'FARP', ['underAttack'] = false, ['status'] = 'closed', ['priority'] = 'strategic', ['units'] = 0, ['coa'] = 'neutral', }, ['Antenna Balkariya'] = { ['type'] = 'Communication', ['underAttack'] = false, ['status'] = 'closed', ['priority'] = 'strategic', ['units'] = 3, ['coa'] = 'red', }, ['Antenna Oche'] = { ['type'] = 'Communication', ['underAttack'] = false, ['status'] = 'closed', ['priority'] = 'strategic', ['units'] = 0, ['coa'] = 'neutral', },
问题根源
JSON序列化库无法处理函数类型的值。虽然type(message)返回table,但message或其嵌套子表中存在未被koEngine.TableSerialization打印的函数类型字段——可能是原任务数据的元方法(metamethod)、隐藏字段,或是其他脚本添加的函数属性。
排查方法
1. 递归遍历检查所有元素类型
编写递归函数,遍历整个表结构(包括嵌套子表),打印所有键值对的类型,定位隐藏的函数:
function inspectTable(t, depth) depth = depth or 0 local indent = string.rep(" ", depth) for k, v in pairs(t) do local vType = type(v) env.info(string.format("%s%s: %s", indent, tostring(k), vType)) if vType == "table" then inspectTable(v, depth + 1) elseif vType == "function" then env.info(string.format("%s⚠️ 发现函数类型字段: %s", indent, tostring(k))) end end end -- 在send函数中替换原日志输出,添加检查 function koUDPSocket:send(data, typee) local message = { typee = typee, data = data, } env.info("检查消息表结构:") inspectTable(message) -- env.info(koEngine.TableSerialization(message)) -- 可暂时注释 -- ... 后续序列化代码 end
2. 单独测试可疑子表
针对日志中显示的目标表,单独测试序列化,缩小排查范围:
-- 在loadMissionDataShort末尾添加测试 local testTarget = missionDataShort["Antenna Balkariya"] env.info("测试单个目标表:") inspectTable(testTarget) local success, err = pcall(koUDPSocket.JSON.encode, koUDPSocket.JSON, testTarget) if not success then env.info("序列化失败: "..err) end
3. 检查原表的元方法
DCS的任务数据可能带有元表,其中包含函数类型的元方法,可通过以下代码检查:
-- 检查objectiveTable的元表 local mt = getmetatable(objectiveTable) if mt then env.info("元表内容:") inspectTable(mt) end
最优解决方案
1. 白名单构建+清除元表
显式构建干净的数据表,强制清除元表,避免继承原数据的隐藏字段或元方法:
-- 修改shortData的构建方式 local shortData = setmetatable({ underAttack = objectiveTable.underAttack, coa = objectiveTable.coa, status = objectiveTable.status, priority = objectiveTable.priority, type = categoryName, units = unitCount }, nil) -- 强制清除元表,避免携带原表的元方法 missionDataShort[objectiveName] = shortData
2. 序列化前过滤函数类型
递归过滤掉所有函数类型的字段,确保传入JSON库的表中只有可序列化类型:
function filterFunctions(t) local filtered = {} for k, v in pairs(t) do local vType = type(v) if vType ~= "function" then filtered[k] = vType == "table" and filterFunctions(v) or v end end return filtered end -- 在send函数中使用过滤 function koUDPSocket:send(data, typee) local message = { typee = typee, data = filterFunctions(data), } local jsonString = self.JSON:encode(message).." \n" -- ... 后续发送代码 end
3. 添加错误捕获
使用pcall捕获序列化错误,避免脚本崩溃,同时输出详细错误信息:
function koUDPSocket:send(data, typee) local message = { typee = typee, data = data, } env.info("SENDING:") env.info(koEngine.TableSerialization(message)) local success, result = pcall(function() return self.JSON:encode(message).." \n" end) if success then socket.try(self.socket:sendto(result, self.host, self.port)) env.info("UDP DATA SENT") else env.info("JSON编码错误: "..result) inspectTable(message) -- 出错时自动检查表结构 end end
内容的提问来源于stack exchange,提问作者Jan Intelkor
相关产品推荐
相关产品推荐

