This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


12.1.1 – Saving Tables without Cycles

Our next (and harder) task is to save tables. There are several ways to do that, according to what restrictions we assume about the table structure. No single algorithm is appropriate for all cases. Simple tables not only need simpler algorithms, but the resulting files can be more aesthetic, too.

Our first attempt is as follows:

    function serialize (o)
      if type(o) == "number" then
        io.write(o)
      elseif type(o) == "string" then
        io.write(string.format("%q", o))
      elseif type(o) == "table" then
        io.write("{\n")
        for k,v in pairs(o) do
          io.write("  ", k, " = ")
          serialize(v)
          io.write(",\n")
        end
        io.write("}\n")
      else
        error("cannot serialize a " .. type(o))
      end
    end
Despite its simplicity, that function does a reasonable job. It even handles nested tables (that is, tables within other tables), as long as the table structure is a tree (that is, there are no shared sub-tables and no cycles). A small aesthetic improvement would be to indent occasional nested tables; you can try it as an exercise. (Hint: Add an extra parameter to serialize with the indentation string.)

The previous function assumes that all keys in a table are valid identifiers. If a table has numeric keys, or string keys which are not syntactic valid Lua identifiers, we are in trouble. A simple way to solve this difficulty is to change the line

          io.write("  ", k, " = ")
to
          io.write("  [")
          serialize(k)
          io.write("] = ")
With this change, we improve the robustness of our function, at the cost of the aesthetics of the resulting file. Compare:
    -- result of serialize{a=12, b='Lua', key='another "one"'}
    -- first version
    {
      a = 12,
      b = "Lua",
      key = "another \"one\"",
    }
    
    -- second version
    {
      ["a"] = 12,
      ["b"] = "Lua",
      ["key"] = "another \"one\"",
    }
We can improve this result by testing for each case whether it needs the square brackets; again, we will leave this improvement as an exercise.