Fun with json encode and decode

TL;DR Encoding a large table to json and then decoding it turns the index into a string, such that I can’t do myTable[1][15], it has to be myTable[1][“15”] instead.

I have a multi dimensional table with up to 15 rows and columns that I want to encode using json. It’s essentially a sparse matrix so not every coordinate contains information.

Here is an example of my table:

{{[3] = {object = {name = “wall”}}}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}

json.encode() will produce something like this which is fine and works well when it’s decoded.

[[null,null,{“object”:{“name”:“wall”}}],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]

 

The problem comes when I have table like this:

{{[15] = {object = {name = “wall”}}}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}

Instead of filling in the missing values with null like the previous example, json.encode() produces this:

[{“15”:{“object”:{“name”:“wall”}}},[],[],[],[],[],[],[],[],[],[],[],[],[],[]]

 

But when it gets decoded it turns into this:

{{[“15”] = {object = {name = “wall”}}}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}

 

Meaning that I can’t access that object using myTable[1][15], because the index has been turned into a string. I have to access it using myTable[1][“15”].

Obviously this is not ideal. Am I doing something wrong? Is there some way to gracefully handle this?

I just convert any string keys that can be turned into number keys, and leave all other string keys as they are:

local function toNumericKeys(t) local t2 = {} for k,v in pairs(t) do --check if string key can be turned into a number if tonumber(k) and type(k) ~= "number" then t2[tonumber(k)] = v else t2[k] = v end end return t2 end local stringKeyedTableFromJson = {["3"] = "hello", ["12"] = "world", ["foo"] = "bar"} local numericallyKeyedTable = toNumericKeys(stringKeyedTableFromJson) --table is now {[3] = "hello", [12] = "world", ["foo"] = "bar"}

My example isn’t recursive, so you’d need to repeat this for tables inside tables, but it gives you an idea.

Hmm, interesting approach. Thanks for sharing!

If you JSON encode a Lua table that has integer keys and string keys and then subsequently decode it, the resultant Lua table will have all string keys. 

You’ll need to convert the stringified numeric keys back to numbers.

This is simply the way the JSON encoder works, and AFAIK you can’t get around this.

It’s a real shocker the first time you notice it happening, but not a huge problem to rectify.

Just updated my function.

Changed this:

if tonumber(k) then

to this:

if tonumber(k) and type(k) ~= "number" then

This stops it from deleting entries which are already numbers.

I just convert any string keys that can be turned into number keys, and leave all other string keys as they are:

local function toNumericKeys(t) local t2 = {} for k,v in pairs(t) do --check if string key can be turned into a number if tonumber(k) and type(k) ~= "number" then t2[tonumber(k)] = v else t2[k] = v end end return t2 end local stringKeyedTableFromJson = {["3"] = "hello", ["12"] = "world", ["foo"] = "bar"} local numericallyKeyedTable = toNumericKeys(stringKeyedTableFromJson) --table is now {[3] = "hello", [12] = "world", ["foo"] = "bar"}

My example isn’t recursive, so you’d need to repeat this for tables inside tables, but it gives you an idea.

Hmm, interesting approach. Thanks for sharing!

If you JSON encode a Lua table that has integer keys and string keys and then subsequently decode it, the resultant Lua table will have all string keys. 

You’ll need to convert the stringified numeric keys back to numbers.

This is simply the way the JSON encoder works, and AFAIK you can’t get around this.

It’s a real shocker the first time you notice it happening, but not a huge problem to rectify.

Just updated my function.

Changed this:

if tonumber(k) then

to this:

if tonumber(k) and type(k) ~= "number" then

This stops it from deleting entries which are already numbers.