lua序列化(支持循环引用)
lua序列化
- 支持key类型为string, number
- 支持value类型为string, number, table, boolean
- 支持
循环引用
- 支持加密序列化
- 支持loadstring反序列化
使用示例
local t = { a = , b = }
local g = { c = , d = , t}
t.rt = g
local ser_str = ser(g)
local unser_table = loadstring(ser_str)()
原理详解
采用递归序列化表的方式实现,并且支持循环引用。循环引用支持实现思路参考了云风的序列化.
先说不考虑支持循环引用的简单情况,采用递归方式实现序列化很简单
直接使用type
函数分别判断表中key-value对儿分别是什么数据类型,然后分别处理
序列化key
local keystr = nil
if type(k) == "string" then
keystr = string.format("[\"%s\"]", k)
elseif type(k) == "number" then
keystr = string.format("[%d]", k)
end
序列化value
local valuestr = nil
if type(v) == "string" then
valuestr = string.format("\"%s\"", tostring(v))
elseif type(v) == "number" or type(v) == "boolean" then
valuestr = tostring(v)
elseif type(v) == "table" then
valuestr = table_ser(v)
end
分别处理完key和value直接插入一个表容器中就可以,最后在使用table.concat
连接字符串就可以,这里要说一下lua中拼接字符串是个比较低效的行为,这跟lua字符串实现有关,每拼接都会重新生成一个新串,所以字符串越长拼接会越慢
这里使用table.concat
,不但减少了拼接次数,而且这样拼接效率比较高,因为是调用C拼接,而非Lua字符串的行为
在大字符串连接中,我们应避免..
。应用table来模拟buffer,然后concat得到最终字符串。
return string.format("{%s}", table.concat(container, ","))
精简后的代码
local function table_ser(tablevalue)
-- 记录表中各项
local container = {}
for k, v in pairs(tablevalue) do
-- 序列化key
local keystr = nil
if type(k) == "string" then
keystr = string.format("[\"%s\"]", k)
elseif type(k) == "number" then
keystr = string.format("[%d]", k)
end -- 序列化value
local valuestr = nil
if type(v) == "string" then
valuestr = string.format("\"%s\"", tostring(v))
elseif type(v) == "number" or type(v) == "boolean" then
valuestr = tostring(v)
elseif type(v) == "table" then
valuestr = table_ser(v)
end table.insert(container, string.format("%s=%s", keystr, valuestr))
end
return string.format("{%s}", table.concat(container, ","))
end
支持循环引用
其实普通序列化没什么好说的,重点在于对循环引用
的支持思路参考了云风的实现
使用一个表mark记录所有序列化过的表,并记录其全key(从根表到当前表的全路径key)每次新序列化一个表时,首先查看是否已经序列化过,若没有序列化则直接序列化, 若已经序列化过则处理如下:
在一个表assgin中记录所有循环引用情况,并给出正确赋值(因为循环引用不能直接序列化,
所以间接的在表构造之后赋值),最后可以一起拼接到一起。
table序列化实现如下:
local function table_ser(tablevalue, tablekey, mark, assign)
-- 标记当前table, 并记录其key名
mark[tablevalue] = tablekey
-- 记录表中各项
local container = {}
for k, v in pairs(tablevalue) do
-- 序列化key
local keystr = nil
if type(k) == "string" then
keystr = string.format("[\"%s\"]", k)
elseif type(k) == "number" then
keystr = string.format("[%d]", k)
end -- 序列化value
local valuestr = nil
if type(v) == "string" then
valuestr = string.format("\"%s\"", tostring(v))
elseif type(v) == "number" or type(v) == "boolean" then
valuestr = tostring(v)
elseif type(v) == "table" then
-- 获得从根表到当前表项的完整key, tablekey(代表tablevalue的key), mark[v]代表table v的key
local fullkey = string.format("%s%s", tablekey, keystr)
if mark[v] then table.insert(assign, string.format("%s=%s", fullkey, mark[v]))
else valuestr = table_ser(v, fullkey, mark, assign)
end
end if keystr and valuestr then
local keyvaluestr = string.format("%s=%s", keystr, valuestr)
table.insert(container, keyvaluestr)
end
end
return string.format("{%s}", table.concat(container, ","))
end
调用table的序列化
local function ser(var, enc)
assert(type(var)=="table")
-- 标记所有出现的table, 并记录其key, 用于处理循环引用
local mark = {}
-- 用于记录循环引用的赋值语句
local assign = {}
-- 序列化表, ret字符串必须与后面的loca ret=%s中的ret相同,因为这个ret可能也会组织到结果字符串中。
local ret = table_ser(var, "ret", mark, assign)
local ret = string.format("local ret=%s %s return ret", ret, table.concat(assign, ";"))
return (enc==nil or enc==true) and string.dump(loadstring(ret)) or ret
end
mark:处理循环引用最重要的就是mark表,它记录了已经序列化的表和其完整key路径
assgin: 记录循环引用的后期赋值语句,将这些语句拼接到表构造之外
序列化后就是一个lua的table创建并赋值的代码字符串,所以可以使用loadstring
直接加载,加载后是一个chunk,可以当作函数运行就返回结果
序列化加密
string.dump(loadstring(ret))
这就是加密的代码,因为string.dump
参数必须是function
, 所以使用loadstring
将字符串加载成chunk,然后在由string.dump
导成字节码
其实就是使用了string.dump
函数,它可以把function
导成二进制字节码,使用它处理一下就可以把明文字符串转成字节码了
完整代码见我github中的luaser
lua序列化(支持循环引用)的更多相关文章
- 解决MVC Json序列化的循环引用问题/EF Json序列化循引用问题---Newtonsoft.Json
1..Net开源Json序列化工具Newtonsoft.Json中提供了解决序列化的循环引用问题: 方式1:指定Json序列化配置为 ReferenceLoopHandling.Ignore 方式2: ...
- EntityFramework中Json序列化的循环引用问题解决--Newtonsoft.Json
1.在使用EF时,由于数据库主外键关联,将对象进行Json序列化时会遇到循环引用的问题 //EF 中由于数据库主外键关联,对象的序列化经常出现循环引用问题 //使用.Net 自带的序列化工具,序列化出 ...
- 一个C#序列化时循环引用的问题
以前一直没搞懂为什么C#在做对象序列化时(Json序列化,XML序列化等)有时候会出现循环引用的问题,下面写了个例子,类People有一个属性引用了类Child,而类Child也有一个属性引用了类Pe ...
- Wcf序列化的循环引用问题1
1.Wcf数据契约序列化,使用的类DataContractSerializer 默认如果类不指定[DataContract],则序列化类的所有字段,并且在出现循环引用的时候回抛出异常,服务终止 msd ...
- Xml序列化自引用/循环引用问题2
1.类定义: public class Student { public int ID { get; set; } public string Name { get; set; } //[XmlIgn ...
- Xml序列化自引用/循环引用问题1
1.定义类 public class Student { public int ID { get; set; } public string Name { get; set; } //[XmlIgno ...
- 解决.Net MVC EntityFramework Json 序列化循环引用问题.
以前都是到处看博客,今天小菜也做点贡献,希望能帮到大家. 废话不多说,直接进入正题. 用过.net MVC的同学应该都被json序列化报循环引用错误这个问题骚扰过.网上有一些解决办法,但是都治标不治本 ...
- WebApi-JSON序列化循环引用
Overview 最近被序列化,循环引用的问题,让我浑身酸爽.遇到这种异常是在搭建WebApi的时候,当我返回Linq实例类集合的时候出现的. 下定决心要解决这个问题.循环引用引起的原因是: 比如说: ...
- Samples DataBind FastJson循环引用问题
Fastjson full support databind, it's simple to use. Encode import com.alibaba.fastjson.JSON; Group g ...
随机推荐
- linux消息队列的使用及内核实现原理
mq_receive NAME mq_open - open a message queue SYNOPSIS #include <fcntl.h> /* For O_* constant ...
- ExpandableListView实现子Item的点击事件
在继承的BaseExpandableListAdapter的ExpandableListView的Adapter中,重写以下方法 @Override public boolean isChildSel ...
- [转]PHP高效率写法(详解原因)
1.尽量静态化: 如果一个方法能被静态,那就声明它为静态的,速度可提高1/4,甚至我测试的时候,这个提高了近三倍. 当然了,这个测试方法需要在十万级以上次执行,效果才明显. 其实静态方法和非静 ...
- Smart Tip
构建了一个smar-tip的tooltip组件 主要特性 自动定位与边界查找 支持8个方向与圆角 自定义皮肤 调用(Usage) var $container = $("#container ...
- DropzoneJS 使用指南
原文链接http://segmentfault.com/a/1190000004045240 官方文档: http://www.dropzonejs.com/ Github: https://gith ...
- nginx 反向代理 取得真实IP和域名
nginx反向代理后,在应用中取得的ip都是反向代理服务器的ip,取得的域名也是反向代理配置的url的域名,解决该问题,需要在nginx反向代理配置中添加一些配置信息,目的将客户端的真实ip和域名传递 ...
- 【转】简单的 Laravel 5 REST API
Introduction Almost all successful internet based companies have APIs. API is an acronym for Applica ...
- <from>;<input>;readonly:只读;disabled:不可用;checked:用来做选中;selected:用在下拉列表中,设置那一项被选中
表单元素: ①②③④⑤⑥⑦★ 一.<from> 1. 代表表单 2 . action:提交为哪个页面 method:① get :显示提交,由长度限制 ② post: 隐藏提交 二.文 ...
- ASP.Net网站程序在编译发布部署后的后期修改
ASP.Net网站程序在发布部署后的后期修改 作者:东篱南山 这里说的后期修改是指网站编译发布并部署好之后,对程序进行的修改,即在不能更改现有代码的情况下,更改页面的显示或是更改业务逻辑.一般是在程序 ...
- 蓝牙BLE MTU规则与约定
1. 问题引言: 想在gatt client上(一般是手机上)传输长一点的数据给gatt server(一般是一个Bluetooth smart设备,即只有BLE功能的设备),但通过 writeCha ...