Lua 实现JSON解析器

JSON

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。
  • JSON 具有自我描述性,更易理解

JSON的组成

JSON 数据的书写格式是:

key : value

JSON 值可以是:

  • 数字(整数或浮点数)
  • 字符串(在双引号中)
  • 逻辑值(true 或 false)
  • 数组(在中括号中)
  • 对象(在大括号中)
  • null

JSON 解析器实现原理

JSON 解析器从本质上来说就是根据 JSON 文法规则创建的状态机

输入是一个 JSON 字符串,输出是一个Table。

状态机具体如下图所示:
Lua 实现JSON解析器_第1张图片
Lua 实现JSON解析器_第2张图片
Lua 实现JSON解析器_第3张图片

具体代码实现

JsonLoader = {}
i = 1
json = ''

function JsonLoader:getChar(str, i)
    --print(str, i)
    return string.sub(str, i, i)

end

-- 跳过空格、换行、制表符、回车
function JsonLoader:skipWhitespace()
    while (JsonLoader:getChar(json, i) == ' '
            or JsonLoader:getChar(json, i) == '\n'
            or JsonLoader:getChar(json, i) == '\t'
            or JsonLoader:getChar(json, i) == '\r') do
        i = i + 1
    end

end

-- 处理逗号
function JsonLoader:eatComma()
    if (JsonLoader:getChar(json, i) ~= ',') then
        print('Expected ,')
    end
    i = i + 1

end

-- 处理冒号
function JsonLoader:eatColon()
    if (JsonLoader:getChar(json, i) ~= ':') then
        print('Expected :')
    end
    i = i + 1
end

-- 是否为16进制数
function JsonLoader:isHexadecimal(char)
    return (char >= '0' and char <= '9') or (string.lower(char) >= 'a' and string.lower(char) <= 'f')
end


-- 获取字符串
function JsonLoader:parseString()
    --print('parse String: ', i , 'times')
    if (JsonLoader:getChar(json, i) == '"') then
        i = i + 1
        local result = ""

        while (JsonLoader:getChar(json, i) ~= '"') do
            if (JsonLoader:getChar(json, i) == '\\') then
                char = JsonLoader:getChar(json, i+1)
                if ( char == '"'
                        or char == '\\'
                        or char == '/'
                        or char == 'b'
                        or char == 'f'
                        or char == 'n'
                        or char == 'r'
                        or char == 't') then
                    result = result..char
                    i = i + 1
                elseif (char == 'u') then
                    if (isHexadecimal(getChar(json, i+2))
                            and isHexadecimal(JsonLoader:getChar(json, i+3))
                            and isHexadecimal(JsonLoader:getChar(json, i+4))
                            and isHexadecimal(JsonLoader:getChar(json, i+5))) then
                        result = result .. string.sub(json,i+2, i+5)
                        i = i + 5
                    end

                end

            else
                result = result .. JsonLoader:getChar(json, i)
                -- 如果字符串为数字,进行转换
                if tonumber(result) ~= nil then
                    result = tonumber(result)
                end

            end

            i = i + 1

        end

        i = i + 1
        --print("parse String res: ", result)
        return result

    end
    return nil
end

-- 解析数值
function JsonLoader:parseNumber()
    --print('parse Number: ', i , 'times')
    start = i
    if JsonLoader:getChar(json, i) == '-' then
        i = i + 1
    end

    -- 解析数字,跳过首位0
    if (JsonLoader:getChar(json, i) == '0') then
        i = i +1
    elseif (JsonLoader:getChar(json, i) >= '1' and JsonLoader:getChar(json, i) <= '9') then
        i = i + 1
        while(JsonLoader:getChar(json, i) >= '0' and JsonLoader:getChar(json, i) <= '9') do
            i = i + 1
        end

    end

    -- 检测小数
    if(JsonLoader:getChar(json, i) == '.') then
        i = i + 1
        while(JsonLoader:getChar(json, i) >= '0' and JsonLoader:getChar(json, i) <= '9') do
            i = i + 1
        end
    end

    -- 检测科学计数法
    if(JsonLoader:getChar(json, i) == 'e' or JsonLoader:getChar(json, i) == 'E') then
        i = i + 1
        if(JsonLoader:getChar(json, i) == '-' or JsonLoader:getChar(json, i) == '+') then
            i = i + 1
        end

        while(JsonLoader:getChar(json, i) >= '0' and JsonLoader:getChar(json, i) <= '9') do
            i = i + 1
        end

    end

    if (i > start) then
        --print('parse Number res: ', tonumber(string.sub(json, start, i-1)))
        return tonumber(string.sub(json, start, i-1))
    end

    return nil

end

-- 解析关键字
function JsonLoader:parseKeyword(name, value)
    if (string.sub(json, i, i + #name) == name) then
        i = i + #name
        return value
    end

end

-- 解析对象
function JsonLoader:parseObject()
    --print('parse Object: ', i , 'times')
    if (JsonLoader:getChar(json, i)== '{') then
        i = i + 1
        JsonLoader:skipWhitespace()
        local result = {}
        initial = true

        while (JsonLoader:getChar(json, i) ~= '}') do
            if (not initial) then
                JsonLoader:eatComma()
                JsonLoader:skipWhitespace()
            end

            local key = JsonLoader:parseString()
            JsonLoader:skipWhitespace()
            JsonLoader:eatColon()

            local value = JsonLoader:parseValue()
            --print(key, value)
            result[key] = value
            initial = false
            JsonLoader:skipWhitespace()

        end
        -- move to the next character of '}'
        i = i + 1

        --for k,  v in pairs(result) do
        --    print('202', k , v)
        --
        --end
        --print('parse Object res: ', result)
        return result

    end
end

-- 解析值 递归调用其他方法
function JsonLoader:parseValue()
    --print('parse Value: ', i , 'times')
    JsonLoader:skipWhitespace()
    local value

    local string = JsonLoader:parseString()
    local number = JsonLoader:parseNumber()
    local object = JsonLoader:parseObject()
    local array = JsonLoader:parseArray()
    local tr = JsonLoader:parseKeyword('true', true)
    local fl = JsonLoader:parseKeyword('false', false)
    local ni = JsonLoader:parseKeyword('null', nil)

    if string ~= nil then
        return string
    elseif number ~= nil then
        return number
    elseif object ~= nil then
        return object
    elseif array ~= nil then
        return array
    elseif tr == true then
        return tr
    elseif fl == false then
        return fl
    elseif ni == nil then
        return ni

        end


        --local value = JsonLoader:parseString() or JsonLoader:parseNumber()
        --        or JsonLoader:parseObject() or JsonLoader:parseKeyword('true', true)
        --        or JsonLoader:parseKeyword('false', false) or JsonLoader:parseKeyword('null', nil)

        JsonLoader:skipWhitespace()
        --print('parse Value res:', value)
        return value

end



-- 解析数组
function JsonLoader:parseArray()
    --print('parse Array: ', i , 'times')
    if (JsonLoader:getChar(json, i) == '[') then
        i = i + 1
        JsonLoader:skipWhitespace()

        local result = {}
        initial = true

        while (JsonLoader:getChar(json, i) ~= ']') do
            if (not initial) then
                JsonLoader:eatComma()
            end

            local value = JsonLoader:parseValue()
            table.insert(result, value)
            initial = false


        end
        i = i + 1

        return result


    end
    return nil


end


-- 解析主函数
function JsonLoader:parse(input_file)
    print('start json parse')
    --res = {}
    --print(input_file)
    -- 读取文件
    local file = io.open(input_file, 'r')
    json = file:read('*a')
    file:close()
    print(json)

    -- json parse
    --print(JsonLoader:getChar(json, i))

    -- 判断首字符是否为'{'
    if (JsonLoader:getChar(json, i) == '{') then

        i = i + 1
        JsonLoader:skipWhitespace()

        -- 保存结果
        local result = {}

        initial = true

        while (JsonLoader:getChar(json, i) ~= '}')
        do
            if not initial then
                JsonLoader:eatComma()
                JsonLoader:skipWhitespace()
            end

            local key = JsonLoader:parseString()
            JsonLoader:skipWhitespace()
            JsonLoader:eatColon()
            local value = JsonLoader:parseValue()
            --print("parse key- value: ",key, value)
            result[key] = value

            --print("273 res:", result[key])
            JsonLoader:skipWhitespace()

            initial = false

        end

        -- move to the next character '{'
        i = i + 1

        return result

    end

    return nil
end


input_file = 'a.json'

local mytable = JsonLoader.parse(nil, input_file)

print(' ---------------JsonLoader  res -------------')
print(type(mytable))
print(mytable[1].a)

--[[
a.json
{"1":{"a":100}}

output: 
table
100
]]--

参考资料

JSON Parser with JavaScript

你可能感兴趣的:(Lua,学习,lua)