lovingxiaobing 发表于 2019-1-18 18:59

[Lua]C++11写的Lua操作Json的C扩展模块



好久没有写东西,在Github逛到了一个Json解释器,叫"Json11",粗略的看了一下,感觉使用挺简单的,于是就把它封装成Lua模块了,经测试g++和msvc都可以编译,有mac或者Linux的朋友可以拿去编译来用用,压缩包里有一个已经编译好的模块,我只编译了windows下的。


使用GNU套件编译时用g++进行编译,加上 -std=c++11
先编译json11再编译Ljson,最后链接生成目标文件


使用visual studio(msvc)的朋友直接把文件都扔进工程里然后编译即可。


因为我使用的时codeblocks+msvc,所以使用codeblocks的朋友直接打开codeblocks的工程(LJson.cbp)即可编译或者查看文件结构。


编译以来于Lua提供的动态库以及动态库的符号连接静态库,以及相关的头文件(费话.....)


下面是一些简单的测试结果:









源代码本来想贴出来的,后来发现400多行占地太大了,但是我还是贴出来吧


/*********************************************
--作者: 小冰哟
--游戏: lovingxiaobing@qq.com
--备注: Json11非原创,
--      Json11是Github上的一个Json解释器项目
--      小冰的LJson是对Json11的 一个封装.
--Json11: https://github.com/dropbox/json11
**********************************************/


#include "LJson.hpp"

#pragma comment(lib, "lua53.lib")

/// 其他一般类型的值压入Lua调用栈
void insert_value_in_table(lua_State* L, const json11::Json& json_in_value) {
    switch (json_in_value.type()) {
    case json11::Json::Type::NUMBER:
      {
            ///数值
            lua_pushnumber(L, (lua_Number)(json_in_value.number_value()));
      }
      break;
    case json11::Json::Type::BOOL:
      {
            ///布尔值
            lua_pushboolean(L, (int)(json_in_value.bool_value()));
      }
      break;
    case json11::Json::Type::STRING:
      {
            ///字符串
            const std::string& str_in_json = json_in_value.string_value();
            lua_pushlstring(L, str_in_json.c_str(), str_in_json.length());
      }
      break;
    case json11::Json::Type::NUL:
      {
            ///空值(NULL)
            lua_pushnil(L);
      }
      break;
    default:
      break;
    }
}


///将Json11对象转换为lua表
void ParseJson11ToLuaTable(
      lua_State* L,
      const json11::Json& json_object,
      int tb_index,
      int&lua_stack_length) {

    ///预留至少三个空闲元素空间(Lua调用栈)
    if (lua_stack_length - lua_gettop(L) < 3) {
      lua_checkstack(L, 3);
      lua_stack_length += 3;
    }

    ///判断是否是可迭代对象
    switch (json_object.type()) {
    /// 数组或者Json对象
    case json11::Json::Type::ARRAY:
      {
            const json11::Json::array& arr = json_object.array_items();

            /// 创建一张足够容量的空表
            lua_createtable(L, arr.size(), 0);
            int now_tb_index = lua_gettop(L);

            lua_pushstring(L, "__name");
            lua_pushstring(L, LJSON_ARRAY);
            lua_rawset(L, now_tb_index);

            for (size_t index = 0; index < arr.size(); ++index) {
                ///获取数组内容
                const json11::Json& json_in_value = json_object;

                ///如果是可迭代对象,递归这个对象
                if (json_in_value.is_array() || json_in_value.is_object()) {
                  /// 执行后获得到的值在栈顶
                  ParseJson11ToLuaTable(L, json_in_value, now_tb_index, lua_stack_length);
                } else {
                  ///是其他的一般类型
                  insert_value_in_table(L, json_in_value);
                }

                /// Lua数组第一个元素是1
                lua_rawseti(L, now_tb_index, (lua_Integer)(index+1));
            }

            ///!若是空数组则表内无元素
      }
      break;
    case json11::Json::Type::OBJECT:
      {
            ///是个不可描述的对象(key-value)
            const json11::Json::object& j_obj = json_object.object_items();

            lua_createtable(L, j_obj.size(), 0);
            int now_tb_index = lua_gettop(L);

            lua_pushstring(L, "__name");
            lua_pushstring(L, LJSON_OBJECT);
            lua_rawset(L, now_tb_index);

            for (const auto& kv: j_obj) {
                const std::string& key = kv.first;
                const json11::Json& value = kv.second;

                lua_pushstring(L, key.c_str());

                if (value.is_array() || value.is_object()) {
                  ParseJson11ToLuaTable(L, value, now_tb_index, lua_stack_length);
                } else {
                  insert_value_in_table(L, value);
                }

                /// 插入key-value, value位于栈顶
                lua_rawset(L, now_tb_index);
            }
      }
      break;
    default:
      {
            ///其他类型
            insert_value_in_table(L, json_object);
            size_t random_index = lua_rawlen(L, tb_index);
            lua_rawseti(L, tb_index, (lua_Integer)(++random_index));
      }
      break;
    }
}


///从文件加载
///参数1: JSON数据(文本文件)的路径
///LJson_load 调用 LJson_loads 进行解析,返回值与LJson_loads一样
int LJson_load(lua_State* L) {

    const char* json_file = luaL_checkstring(L, 1);

    ///POSIX标准
    ///检查文件是否存在
    if (!access(json_file, F_OK)) {
      ///判断文件是否可读
      if (access(json_file, R_OK)) {
            luaL_error(L, "No Reading access: %s\n", json_file);
      }
    } else {
      luaL_error(L, "No found: %s\n", json_file);
    }

    ///打开文件(文本方式和只读)
    std::fstream fs_json(json_file, std::ios::in);

    if (false == fs_json.is_open()) {
      luaL_error(L, "Json File Open Error!\n");
    }

    std::stringstream buf;
    ///读入文件
    buf<<fs_json.rdbuf();

    if (!fs_json.good())
      luaL_error(L, "\"%s\" Read Error!", json_file);

    ///将读入的JSON字符串压入Lua调用栈
    lua_settop(L, 0);
    std::string json_str = buf.str();
    lua_pushlstring(L, json_str.c_str(), json_str.length());

    int LJson_loads(lua_State* L);
    return LJson_loads(L);
}

///参数1: JSON字符串
///返回值: 与JSON结构相同但"键-值"顺序不一定相同的的table
int LJson_loads(lua_State* L) {

    const char* c_json_str = luaL_checkstring(L, 1);

    //从获取的JSON字符串解析成Json11对象
    std::string err;
    json11::Json LJson_Object = json11::Json::parse(std::string(c_json_str), err);

    lua_settop(L, 0);

    if (LJson_Object.is_object() || LJson_Object.is_array()) {
      ///JSON根节点(lua表)
      lua_newtable(L);
      ///给定一个lua调用栈的默认大小
      int stk_len = LUA_MINSTACK;
      ParseJson11ToLuaTable(L, LJson_Object, 1, stk_len);
    } else {
      luaL_error(L, "Error: %s\n", err.c_str());
    }

    ///只有成功,不许失败
    return 1;
}


///检查元素类型
LJSON_ITERATOR_OBJECT_TYPE check__name_key(lua_State* L, int ele_index) {
    const char* key = "__name";
    const char* cp_value = LJSON_ARRAY;

    /// 首先得是一张表
    if (lua_istable(L, ele_index)) {
      ///获取当前栈顶位置,方便恢复
      int before_top = lua_gettop(L);
      ///如果__name的值是字符串类型则进行进一步判断是否是"LJSON_ARRAY"
      if (LUA_TSTRING == lua_getfield(L, ele_index, key)) {
            const char* value = lua_tostring(L, lua_gettop(L));
            ///如果value==cp_value成立则为"LJSON_ARRAY",否则默认为"LJSON_OBJECT"
            if (0 == strcmp(value, cp_value)) {
                lua_settop(L, before_top);
                return I_LJSON_ARRAY;
            }
      }

      lua_settop(L, before_top);
      /// "LJSON_OBJECT",nil默认为"LJSON_OBJECT"
      return I_LJSON_OBJECT;
    }

    ///不是表则为默认类型
    return I_LJSON_DEFAULT;
};


///将表值插入Json11对象
void insert_value_in_json(lua_State* L, json11::Json& json_element) {

    switch (lua_type(L, -1)) {
    case LUA_TSTRING:
      {
            ///字符串
            const char* str = lua_tostring(L, -1);
            json_element = json11::Json(str);
      }
      break;
    case LUA_TNUMBER:
      {
            if (lua_isinteger(L, -1)) {
                ///整数
                lua_Integer integer_number = lua_tointeger(L, -1);
                json_element = json11::Json((int)integer_number);
            } else {
                ///浮点数
                lua_Number float_number = lua_tonumber(L, -1);
                json_element = json11::Json((double)float_number);
            }
      }
      break;
    case LUA_TBOOLEAN:
      {
            ///布尔值
            bool boolean_v = (bool)lua_toboolean(L, -1);
            json_element = json11::Json(boolean_v);
      }
      break;
    case LUA_TNIL:
      {
            ///空值
            json_element = json11::Json(nullptr);
      }
      break;
    case LUA_TUSERDATA:
    case LUA_TLIGHTUSERDATA:
    case LUA_TTHREAD:
    case LUA_TFUNCTION:
      {
            ///是上面的一大摞类型直接转换成相应的形式(会以字符串的形式展示)

            std::string other_value;
            lua_pushfstring(L, "%s:%p", lua_typename(L, lua_type(L, -1)), lua_topointer(L, -1));
            other_value.append(lua_tostring(L, -1));
            lua_pop(L, 1);
            json_element = json11::Json(other_value);
      }
      break;
    default:
      break;
    }
}

/// 将Lua表转换为Json11对象
/// !!!
/// 表的"__name"来注明表的类型:
/// "LJSON_ARRAY": 数组
/// "LJSON_OBJECT": 不可描述的对象
/// 没有"__name"键和值默认将表作为"LJSON_OBJECT"处理
/// "LJSON_OBJECT"将键统一转换为字符串
/// 因为Lua表是哈希表,元素的顺序不能确定
/// 只要是"LJSON_ARRAY"类型,那么根据表的长度迭代获取元素
void LuaTableToJson11(
            lua_State* L,
            json11::Json& json_object,
            int tb_index
) {
    ///
    switch (check__name_key(L, tb_index)) {
    case I_LJSON_ARRAY:
      {
            ///数组
            lua_len(L, tb_index);
            lua_Integer tb_for_array_len = lua_tointeger(L, -1);
            lua_settop(L, tb_index);

            json11::Json::array arr((const unsigned int)tb_for_array_len);

            for (int i = 1; i <= tb_for_array_len; ++i) {
                if (LUA_TTABLE == lua_geti(L, tb_index, i)) {
                  LuaTableToJson11(L, arr, lua_gettop(L));
                } else {
                  insert_value_in_json(L, arr);
                }

                ///删除栈顶元素进行下一轮循环
                lua_pop(L, 1);
            }

            json_object = arr;
      }
      break;
    case I_LJSON_OBJECT:
      {
            ///不可描述的对象

            json11::Json::object map_json;

            ///起始键
            lua_pushnil(L);
            while (lua_next(L, tb_index)) {
                ///不管什么类型,转换成string类型就对了
                std::string key;
                switch (lua_type(L, -2)) {
                case LUA_TNUMBER:
                case LUA_TSTRING:
                case LUA_TBOOLEAN:
                  {
                        key = lua_tostring(L, -2);

                  }
                  break;
                case LUA_TTABLE:
                case LUA_TTHREAD:
                case LUA_TUSERDATA:
                case LUA_TLIGHTUSERDATA:
                case LUA_TFUNCTION:
                  {
                        lua_pushfstring(L, "%s:%p", lua_typename(L, lua_type(L, -2)), lua_topointer(L, -2));
                        key = lua_tostring(L, -1);
                        lua_pop(L, 1);
                  }
                  break;
                default:
                  ///不存在nil键
                  break;
                }

                /// 忽略__name键
                if ("__name" == key){
                  lua_pop(L, 1);
                  continue;
                }

                if (lua_istable(L, -1)) {
                  map_json = json11::Json();
                  LuaTableToJson11(L, map_json, lua_absindex(L, -1));
                } else {
                  insert_value_in_json(L, map_json);
                }
                ///把值丢掉,留着键做下一次迭代
                lua_pop(L, 1);
            }

            json_object = map_json;
      }
      break;
    case I_LJSON_DEFAULT:
    default:
      {
            ///默认一般类型
            insert_value_in_json(L, json_object);
      }
      break;
    }
}


/// 参数1: 文件路径(字符串)
/// 参数2: 表
/// 返回值: 成功返回true, 失败返回false
/// 以覆盖的方式写入
int LJson_dump(lua_State* L) {

    const char* json_save_path = luaL_checkstring(L, 1);
    luaL_checktype(L, 2, LUA_TTABLE);

    std::fstream fs_json(json_save_path, std::ios::out);

    if (false == fs_json.is_open()) {
      //luaL_error(L, "File Open|Create Error!");
      lua_pushboolean(L, false);
    } else {
      lua_replace(L, 1);
      lua_settop(L, 1);
      json11::Json json_object;

      LuaTableToJson11(L, json_object, 1);

      std::string json_str;
      json_object.dump(json_str);

      ///写入文件
      fs_json<<json_str;

      lua_pushboolean(L, fs_json.good());

      fs_json.clear();
    }

    return 1;
}

/// 参数1: 表
/// 返回值: JSON字符串
int LJson_dumps(lua_State* L) {

    ///检查第一个参数是否是表
    luaL_checktype(L, 1, LUA_TTABLE);

    ///把多余的参数丢掉
    lua_settop(L, 1);

    json11::Json json_object;
    LuaTableToJson11(L, json_object, 1);

    std::string json_str;
    ///导出成字符串
    json_object.dump(json_str);

    lua_pushstring(L, json_str.c_str());

    return 1;
}

#define MAKELJSONMETHOD(name) {#name, LJson_##name}
static struct luaL_Reg LJson_method[] = {
    MAKELJSONMETHOD(load),///从文件获得JSON字符串转换为table
    MAKELJSONMETHOD(loads), ///从JSON字符串转换为table
    MAKELJSONMETHOD(dump),///table导出为JSON文本文件
    MAKELJSONMETHOD(dumps), ///table导出为JSON字符串
    {nullptr, nullptr}
};

LJSON_EXPORT int luaopen_LJson(lua_State* L) {
    luaL_newlib(L, LJson_method);

    int method_table = lua_gettop(L);

    //"LJSON_ARRAY"
    lua_pushstring(L, LJSON_ARRAY);
    lua_pushstring(L, LJSON_ARRAY);
    lua_rawset(L, method_table);

    //"LJSON_OBJECT"
    lua_pushstring(L, LJSON_OBJECT);
    lua_pushstring(L, LJSON_OBJECT);
    lua_rawset(L, method_table);

    return 1;
}


这个是主要的源代码,其他的部分都在压缩包里。

源代码以及例子:



demo886 发表于 2019-1-18 19:10

感谢分享

小小吴 发表于 2019-1-18 19:25

感谢分享

key4479 发表于 2019-1-18 19:36

感谢分享!楼主辛苦了!

一个路过的康米 发表于 2020-11-17 15:14

大佬,能问个问题吗?我现在有个C实现的校验功能,希望能加载到Wireshark自带的lua中,该怎么去实现呢?
页: [1]
查看完整版本: [Lua]C++11写的Lua操作Json的C扩展模块