释放json转lua对象后的字段各种校验

背景:

在实际项目中,经常遇到json字符串转为lua对象后,需要判断字段是否缺失、是否与定义的类型保持一致。

不同对象需要分别判断,需要写大量重复代码,效率低,而且容易出错,所以这里实现一种可行且高效的方式。

使用案例演示:

Lua 复制代码
--如果字段不是必须要检验项,可以构造时给false
--普通对象
local Cat = {
    male = JsonLuaValidator.bool(),
    age = JsonLuaValidator.int(),
    speed = JsonLuaValidator.number(),
    food = JsonLuaValidator.string()
}
local RefCat = JsonLuaValidator.object(Cat)

--对象内嵌对象
local Animal = {
    addr = JsonLuaValidator.string(),
    cat = RefCat,
    arrcat = JsonLuaValidator.array(RefCat)
}
local RefAnimal = JsonLuaValidator.object(Animal)

--数组
local RefArrayAnimal = JsonLuaValidator.array(RefAnimal)

--普通变量示例------------------------------------------------------------------
local obj = "abc"
print(JsonLuaValidator.check(obj, JsonLuaValidator.string(), "data"))

--普通对象示例------------------------------------------------------------------
local str = [[
{
    "male":true,
    "age":1,
    "speed":1.2,
    "food":"fish"
}
]]
local obj = json.decode(str)
print(JsonLuaValidator.check(obj, RefCat, "data"))

--对象嵌套示例------------------------------------------------------------------
local str = [[
{
    "addr":"china",
    "cat":{
        "male":true,
        "age":1,
        "speed":1.2,
        "food":"fish"
    },
    "arrcat":[
        {
            "male":false,
            "age":1,
            "speed":1.2,
            "food":"fish"
        },
        {
            "male":true,
            "age":2,
            "speed":1.2,
            "food":"fish"
        }
    ]
}
]]
local obj = json.decode(str)
print(JsonLuaValidator.check(obj, RefAnimal, "data"))

--数组对象示例------------------------------------------------------------------
local str = [[
[
    {
        "addr":"china",
        "cat":{
            "male":true,
            "age":1,
            "speed":1.2,
            "food":"fish"
        },
        "arrcat":[
            {
                "male":false,
                "age":1,
                "speed":1.2,
                "food":"fish"
            },
            {
                "male":true,
                "age":2,
                "speed":1.2,
                "food":"fish"
            }
        ]
    },
    {
        "addr":"china",
        "cat":{
            "male":true,
            "age":1,
            "speed":1.2,
            "food":"fish"
        },
        "arrcat":[
            {
                "male":false,
                "age":1,
                "speed":1.2,
                "food":"fish"
            },
            {
                "male":true,
                "age":2,
                "speed":1.2,
                "food":"fish"
            }
        ]
    }
]
]]
local obj = json.decode(str)
print(JsonLuaValidator.check(obj, RefArrayAnimal, "data"))

面向对象实现

Lua 复制代码
local JsonLuaValidator = {}

--构造一个`bool`对象
function JsonLuaValidator.bool(bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "bool", --表示变量类型
        req = bIsRequired,  --表示该变量是否为必检项
        v = false --表示该变量的类型值
    }
end

--构造一个`integer`对象
function JsonLuaValidator.int(bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "int", req = bIsRequired, v = 0}
end

--构造一个`float`对象
function JsonLuaValidator.float(bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "float", req = bIsRequired, v = 0.0}
end

--构造一个`number`对象
function JsonLuaValidator.number(bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "number", req = bIsRequired, v = 0}
end

--构造一个`string`对象
function JsonLuaValidator.string(bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "string", req = bIsRequired, v = ""}
end

--构造一个`object`对象,`referenceObject`为参考对象
function JsonLuaValidator.object(referenceObject, bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "object", 
        req = bIsRequired, 
        --[[
        参考对象,表示`referenceObject`对象中必检项必须全都存在于待检测对象中且类型一致,参考对象中的每一个字段必须使用`JsonLuaValidator`进行初始化。
        例如:
        local Student = {
            ok = JsonLuaValidator.bool(),
            age = JsonLuaValidator.int(),
            score = JsonLuaValidator.number(),
            name = JsonLuaValidator.string(),
            sch = JsonLuaValidator.array({""})
        }
        ]]--
        v = referenceObject
    }
end

--构造一个`array`对象,`referenceObject`为数组中的元素参考对象
function JsonLuaValidator.array(referenceObject, bIsRequired)
    if nil==bIsRequired then bIsRequired=true end
    return {t = "array", 
        req = bIsRequired, 
        --[[
        参考对象数组,通常长度为1即可,主要是告诉检测器,这个被检测的数组中的所有类型都是满足这个对象,
        例如:
            {JsonLuaValidator.string()}表示字符串数组、
            {JsonLuaValidator.number()}表示数字数组、
            {JsonLuaValidator.bool()}表示bool数组、
            {JsonLuaValidator.object({a=JsonLuaValidator.bool(),b=JsonLuaValidator.number()})}表示对象数组
        入参`referenceObject`只需要传入一个引用构造对象即可
        ]]--
        v = {referenceObject}
    }
end

--[[
功能:校验json字符串转为lua对象后,字段的缺失与合法性的判断。
参数:luaObj-json字符串转换为lua的对象
      refObject-校验合法性的参考对象,也就是luaObj对象的变异体,变异体本身以及变异体里面的字段变量都需要使用`JsonLuaValidator`进行初始化
      objName-对象名称,通常是顶级对象名称,主要是用来返回失败时,输出哪个字段失败
返回值:boolean,string-分别表示成功与失败、失败原因信息
]]--
function JsonLuaValidator.check(luaObj, refObject, objName)
    objName = objName or ""
    
    --检测refObject必须满足固定格式要求
    if type(refObject)~="table" then
        return false,"inner error:the ref object `"..objName.."` is invalid table"
    end
    if type(refObject.t)~="string" then
        return false,"inner error:the field `t` in ref object `"..objName.."` is not string or not exists"
    end
    if type(refObject.req)~="boolean" then
        return false,"inner error:the field `req` in ref object `"..objName.."` is not boolean or not exists"
    end
    if type(refObject.v)=="nil" then
        return false,"inner error:the field `v` in ref object `"..objName.."` is not exists"
    end
    
    if true~=refObject.req then
        return true,"" --不是必须检验项则默认合法
    end
    
    if refObject.t == "bool" then
        if type(luaObj) ~= "boolean" then
            return false,objName.." is not boolean or not exists"
        end
    elseif refObject.t == "int" then
        if math.type(luaObj) ~= "integer" then
            return false,objName.." is not integer or not exists"
        end
    elseif refObject.t == "float" then
        if math.type(luaObj) ~= "float" then
            return false,objName.." is not float or not exists"
        end
    elseif refObject.t == "number" then
        if type(luaObj) ~= "number" then
            return false,objName.." is not number or not exists"
        end
    elseif refObject.t == "string" then
        if type(luaObj) ~= "string" then
            return false,objName.." is not string or not exists"
        end
    elseif refObject.t == "object" then
        if type(luaObj) ~= "table" then
            return false,objName.." is not table or not exists"
        end
        if type(refObject.v) ~= "table" then
            return false,"inner error:the field `v` in ref object `"..objName.."` is not table or not exists"
        end
        for k,v in pairs(refObject.v) do
            local _ok,err = JsonLuaValidator.check(luaObj[k], v, objName.."."..k)
            if not _ok then
                return false,err
            end
        end
    elseif refObject.t == "array" then
        if type(luaObj) ~= "table" then
            return false,objName.." is not array or not exists"
        end
        if type(refObject.v) ~= "table" then
            return false,"inner error:the field `v` in ref object `"..objName.."` is not array or not exists"
        end
        local newRefObj = refObject.v[1]
        for i=1,#luaObj do
            local _ok,err = JsonLuaValidator.check(luaObj[i], newRefObj, objName.."["..i.."]")
            if not _ok then
                return false,err
            end
        end
    end
    return true,""
end

return JsonLuaValidator