Lua table.toString(tableName) and table.fromString(stringTable) functions?
Asked Answered
D

8

14

I am wanting to convert a 2d lua table into a string, then after converting it to a string convert it back into a table using that newly created string. It seems as if this process is called serialization, and is discussed in the below url, yet I am having a difficult time understanding the code and was hoping someone here had a simple table.toString and table.fromString function
http://lua-users.org/wiki/TableSerialization

Dunstable answered 20/5, 2011 at 16:59 Comment(0)
D
11

The code lhf posted is a much simpler code example than anything from the page you linked, so hopefully you can understand it better. Adapting it to output a string instead of printing the output looks like:

t = {
{11,12,13},
{21,22,23},
}

local s = {"return {"}
for i=1,#t do
  s[#s+1] = "{"
  for j=1,#t[i] do
    s[#s+1] = t[i][j]
    s[#s+1] = ","
  end
  s[#s+1] = "},"
end
s[#s+1] = "}"
s = table.concat(s)

print(s)

The general idea with serialization is to take all the bits of data from some data structure like a table, and then loop through that data structure while building up a string that has all of those bits of data along with formatting characters.

Delozier answered 20/5, 2011 at 17:57 Comment(6)
Thanks for the code. I meant redefining print or using a different function to collect the output in a table so that you could keep my original code mostly intact.Subcontract
I hate overloading functions because I always forget that I changed it.Delozier
I believe #t has to do some iterating to calculate the length of a table. It may be more efficient to use your own length-counter variable.Grand
isn't this answer is only valid for arrays? Assuming your index starts on 1 and follows the sequence... What about general-purpose tables (indexed by whatever values, like strings... aka, Hashtables)?Luz
I would strongly suggest JSON for this purpose (ie. dosimple's answer https://mcmap.net/q/790246/-lua-table-tostring-tablename-and-table-fromstring-stringtable-functions ). My answer more addresses the approach linked in the question, but really a better approach is serializing to JSON.Delozier
this language is maddness.Tuneless
B
25

I am using the following code in order to serialize tables:

function serializeTable(val, name, skipnewlines, depth)
    skipnewlines = skipnewlines or false
    depth = depth or 0

    local tmp = string.rep(" ", depth)

    if name then tmp = tmp .. name .. " = " end

    if type(val) == "table" then
        tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")

        for k, v in pairs(val) do
            tmp =  tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
        end

        tmp = tmp .. string.rep(" ", depth) .. "}"
    elseif type(val) == "number" then
        tmp = tmp .. tostring(val)
    elseif type(val) == "string" then
        tmp = tmp .. string.format("%q", val)
    elseif type(val) == "boolean" then
        tmp = tmp .. (val and "true" or "false")
    else
        tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
    end

    return tmp
end

the code created can then be executed using loadstring(): http://www.lua.org/manual/5.1/manual.html#pdf-loadstring if you have passed an argument to 'name' parameter (or append it afterwards):

s = serializeTable({a = "foo", b = {c = 123, d = "foo"}})
print(s)
a = loadstring(s)()
Blumenthal answered 21/5, 2011 at 12:14 Comment(3)
you know, tostring works on booleans too. tostring(true) == "true"Grand
This code does not handle things like a={}; a[1]=2; however. ({1=2} is not dostring-able) Also for nil etc.. For pretty printing there are already other libraries however.Procyon
For numeric keys like {1=...} I added this line: if type(k) == "number" then k = "[\"" .. tostring(k) .. "\"]" end in the for loop.Baudoin
D
11

The code lhf posted is a much simpler code example than anything from the page you linked, so hopefully you can understand it better. Adapting it to output a string instead of printing the output looks like:

t = {
{11,12,13},
{21,22,23},
}

local s = {"return {"}
for i=1,#t do
  s[#s+1] = "{"
  for j=1,#t[i] do
    s[#s+1] = t[i][j]
    s[#s+1] = ","
  end
  s[#s+1] = "},"
end
s[#s+1] = "}"
s = table.concat(s)

print(s)

The general idea with serialization is to take all the bits of data from some data structure like a table, and then loop through that data structure while building up a string that has all of those bits of data along with formatting characters.

Delozier answered 20/5, 2011 at 17:57 Comment(6)
Thanks for the code. I meant redefining print or using a different function to collect the output in a table so that you could keep my original code mostly intact.Subcontract
I hate overloading functions because I always forget that I changed it.Delozier
I believe #t has to do some iterating to calculate the length of a table. It may be more efficient to use your own length-counter variable.Grand
isn't this answer is only valid for arrays? Assuming your index starts on 1 and follows the sequence... What about general-purpose tables (indexed by whatever values, like strings... aka, Hashtables)?Luz
I would strongly suggest JSON for this purpose (ie. dosimple's answer https://mcmap.net/q/790246/-lua-table-tostring-tablename-and-table-fromstring-stringtable-functions ). My answer more addresses the approach linked in the question, but really a better approach is serializing to JSON.Delozier
this language is maddness.Tuneless
S
10

How about a JSON module? That way you have also a better exchangeable data. I usually prefer dkjson, which also supports utf-8, where cmjjson won't.

Solingen answered 21/5, 2011 at 0:30 Comment(1)
Upvote for making me realize that JSON was the better solution. I already had a JSON library that I was using in the project, even, but I'd forgotten.Embroideress
W
6

Under the kong works this

local cjson = require "cjson"
kong.log.debug(cjson.encode(some_table))

Out of the kong should be installed package lua-cjson https://github.com/openresty/lua-cjson/

Wallachia answered 19/5, 2020 at 6:26 Comment(0)
S
3

Here is a simple program which assumes your table contains numbers only. It outputs Lua code that can be loaded back with loadstring()(). Adapt it to output to a string instead of printing it out. Hint: redefine print to collect the output into a table and then at the end turn the output table into a string with table.concat.

t = {
{11,12,13},
{21,22,23},
}

print"return {"
for i=1,#t do
        print"{"
        for j=1,#t[i] do
                print(t[i][j],",")
        end
        print"},"
end
print"}"
Subcontract answered 20/5, 2011 at 17:47 Comment(0)
V
3

Assuming that:

  • You don't have loops (table a referencing table b and b referencing a)
  • Your tables are pure arrays (all keys are consecutive positive integers, starting on 1)
  • Your values are integers only (no strings, etc)

Then a recursive solution is easy to implement:

function serialize(t)
  local serializedValues = {}
  local value, serializedValue
  for i=1,#t do
    value = t[i]
    serializedValue = type(value)=='table' and serialize(value) or value
    table.insert(serializedValues, serializedValue)
  end
  return string.format("{ %s }", table.concat(serializedValues, ', ') )
end

Prepend the string resulting from this function with a return, store it on a .lua file:

-- myfile.lua
return { { 1, 2, 3 }, { 4, 5, 6 } }

You can just use dofile to get the table back.

t = dofile 'myfile.lua'

Notes:

  • If you have loops, then you will have to handle them explicitly - usually with an extra table to "keep track" of repetitions
  • If you don't have pure arrays, then you will have to parse t differently, as well as handle the way the keys are rendered (are they strings? are they other tables? etc).
  • If you have more than just integers and subtables, then calculating serializedValue will be more complex.

Regards!

Vaunt answered 21/5, 2011 at 8:6 Comment(0)
D
2

I have shorter code to convert table to string but not reverse

function compileTable(table)
   local index = 1
   local holder = "{"
   while true do
      if type(table[index]) == "function" then
         index = index + 1
      elseif type(table[index]) == "table" then
         holder = holder..compileTable(table[index])
      elseif type(table[index]) == "number" then
         holder = holder..tostring(table[index])
      elseif type(table[index]) == "string" then
         holder = holder.."\""..table[index].."\""
      elseif table[index] == nil then
         holder = holder.."nil"
      elseif type(table[index]) == "boolean" then
         holder = holder..(table[index] and "true" or "false")
      end
      if index + 1 > #table then
        break
      end
      holder = holder..","
      index = index + 1
   end
   return holder.."}"
end

if you want change the name just search all compileTable change it to you preferred name because this function will call it self if it detect nested table but escape sequence I don't know if it work

if you use this to create a lua executable file that output the table it will ge compilation error if you put new line and " sequence this method is more memory efficient

Note:

  1. Function not supported
  2. User data I don't know
Danieledaniell answered 1/5, 2020 at 11:50 Comment(0)
H
2

My solution:

local nl = string.char(10) -- newline
function serialize_list (tabl, indent)
    indent = indent and (indent.."  ") or ""
    local str = ''
    str = str .. indent.."{"
    for key, value in pairs (tabl) do
        local pr = (type(key)=="string") and ('["'..key..'"]=') or ""
        if type (value) == "table" then
            str = str..nl..pr..serialize_list (value, indent)..','
        elseif type (value) == "string" then
            str = str..nl..indent..pr..'"'..tostring(value)..'",'
        else
            str = str..nl..indent..pr..tostring(value)..','
        end
    end
    str = str:sub(1, #str-1) -- remove last symbol
    str = str..nl..indent.."}"
    return str
end

local str = serialize_list(tables)
print('return '..nl..str)
Halfhearted answered 8/1, 2021 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.