Fork me on GitHub

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。

状态机具体如下图所示:


具体代码实现

	
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

posted @   ZTianming  阅读(1251)  评论(0编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
欢迎阅读『Lua 实现JSON解析器』

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示