lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hello,
I've seen the thread about serialization earlier in this list,
but didn't find the code mentioned there.

To deal with persistent data in LUA similar to the Win95 INI-Files, I put
together a serialization scheme for basic lua types (Lua version 3.2).
'function' and 'userdata' is not supported.
There is one function 'serialize(t,q)' which returns a _one_ line 
representation of the value of variable t. Parameter q is optional to 
change the default quote character (') of string serialization.
Note that the name of the serialized variable is not included in
the serial representation but only the value.

The other function is 'unserialize(s)' which tries to undo what 
serialize did. It's also possible to unserialize unquoted strings, but 
this can have side effects. Look at attached source for details.

To see an example of how it works, run 
$lua serial.lua >out 
from the command line and look at the generated output in file 'out'.

It's not heavily tested but works fine for me. 
May be it is useful for someone else.
Comments, improvements and bug reports are welcome.

CM

-- 
Sent through GMX FreeMail - http://www.gmx.net
-----------------------------------------------------------------------------
-- Simple serialization of basic lua types
-- 11/12/2000 Christof Muz
-- This is public domain. Absolutely no guarantees.
-----------------------------------------------------------------------------

-----------------------------------
-- Make a _flat_ copy of table t --
-----------------------------------
function duptable(t)
if type(t) == 'table' then
 local nt = {}
 local i,v = next(t,nil)
 while i do
  nt[i]=v
  i,v = next(t,i)
 end
 return nt
end
return t
end

---------------------------------------------
-- Escape all special characters of string --
---------------------------------------------
function escapestring(s)
  s = gsub(s,"([%c\\'\"%z]?)", -- convert special characters
	     function(l)
	       if l and l ~= '' then
		if strbyte(l) == strbyte('\n') then return "\\n" end
		if strbyte(l) == strbyte('\t') then return "\\t" end
		if strbyte(l) == strbyte('\r') then return "\\r" end
		if strbyte(l) == strbyte('\f') then return "\\f" end
		if strbyte(l) == strbyte('\\') then return "\\\\" end
		if strbyte(l) == strbyte('"')  then return '\\"' end
		if strbyte(l) == strbyte("'")  then return "\\'" end
		return "\\" .. tonumber(strbyte(l))
	       end
	     end
	    )
  return s
end

----------------------------------------------------
-- Serialize a luatype (recursively for 'tables') --
-----------------------------------------------------------------------------
-- The serialization of a luatype is a _one_ line string (e.g. no \n,...)
-- ot : the luatype , works fine with 'table', 'string', 'number' and nil
--	for userdata and functions the type string is returned.
-- q (optional) : quote-character for string serialization (default is "'")
-----------------------------------------------------------------------------
function serialize(ot,q)

 if ot == nil or
    type(ot) == 'number' then
    return tostring(ot)
 end

 if not q then -- string quotation character
    q = "'"
 end

 if type(ot) == 'string' then
    ot = escapestring(ot)
    return q .. ot .. q
 end

 if type(ot) == 'table' then -- tables
  local s ='{'           -- serial representation to build
  local t = duptable(ot) -- work on a copy of the table
  local i,n = 1,getn(t)  -- iteration start for 'array' elements
  local sep = ''         -- the item seperator for output
  local defd = '_$visited$_' -- this element is already serialized

  while i<=n do 	 -- first all array items
     if t[i] then  s = s .. sep .. serialize(t[i]) end
     t[i]=defd
     sep = ','
     i=i+1
  end
  sep=';'
  i,v = next(t,nil)	-- iterate again for all other items
  while i do
    if v and v ~= defd then
      s = s .. sep .. tostring(i) .. '=' .. serialize(v)
      sep=','
    end
    i,v = next(t,i)
  end
  s = s .. '}'
  return s
 end
 return q .. type(ot) .. q
end

------------------------
-- Undo serialization --
-----------------------------------------------------------------------------
-- only type 'string' is considered a valid serialization
-- other types are simply returned !
-- serialized strings should be quoted (e.g "'a string'"), which
-- is the default behaviour of serialize.
-- If not quoted the default 'tonumber' conversion may lead to
-- unexpected results. ( e.g. string(1stclass) -> number(1) )
-- Leading whitespace is _NOT_ stripped, which might be a problem with
-- 'table' unserialization, as tables are identified by a '{'-character
-- at the first position. (e.g. string({n=0}) -> table {n=0}
-- but string( {n=0}) represents a string
--	      ^ space-character
-----------------------------------------------------------------------------
function unserialize(s)
if type(s) ~= 'string' then return s end
if tonumber(s) then return tonumber(s) end  -- seems to be a simple number
					    -- but watch out: '1a-class'
if s == tostring(nil) then return nil end   -- nil

if strbyte(s) ~= strbyte('{') and           -- not a tabledef string
   strbyte(s) ~= strbyte("'") and           -- and
   strbyte(s) ~= strbyte('"') then          -- not a quoted string
   local t = escapestring(s)
      s = "'" .. t .. "'"                   -- quote the string
end
 local gname = '__unserialized__var'
 local t
 -- parse string with lua core
 dostring(gname.."="..s)          -- convert it
 t = getglobal(gname)
 setglobal(gname,nil)
 return t
end


-----------------------------------------------------------------------------
-- Tests of serialization/unserialization
-----------------------------------------------------------------------------

function xforeach(t,f,l) -- recursively for tables
local i,v = next(t,nil)
 if not l then l=0 end
 while i do
  if type(v) == 'table' then
      f(l,i,v)
     xforeach(v,f,l+1)
  else
     f(l,i,v)
  end
  i,v = next(t,i)
 end
end

function tp(l,n,v)  -- print table hierarchy (l=level)
local s=''
 while l > 0 do s=s..'\t' l=l-1 end
 print(format("%s[%s]=(%s)%s",s,n,type(v),tostring(v)))
end

-----------------------------------------------------------------------------
--- define test data
-----------------------------------------------------------------------------
tab = {1,2,3,4,
       {5.1,5.2;n=2,t='subtable'},
       {6.1,6.2;n=2,t='subtable'};
       n=6,
       example='table serialization'}

strtab = { " 'first' ",' "second" ', " \"third\" ",' \'fourth\' '}

bindat = [[Hello World
from Lua
This is a longer 'string' with

     embedded ctrl-characters\t
]]

thetest = {tab,strtab,bindat;n=3}

-----------------------------------------------------------------------------
-- The test
-----------------------------------------------------------------------------
--- print test data
print('test data:')
xforeach(thetest,tp)
--- serialize and print serialization
thetestser = serialize(thetest)
print('\nserial representation of test data:')
print(thetestser,"\n")
--- unserialize and print generated data
print('unserialized test data:')
xforeach(unserialize(thetestser),tp)