Trouble with JSON encode / decode and 2D arrays

I am attempting to save elements of my game the standard way, by encoding to JSON, saving to a file, and decoding the file to load the table later. However, this does not seem to work for 2D arrays. I am testing this in the Android Corona Simulator.

My map data is a 2D array of information such as type, image filename, boolean flags, etc…

When I try to save and load the table, the indexing is corrupted slightly.

This works:

 local saveGame = {} saveGame = mapData print(saveGame[1][1].type) utility.saveTable(saveGame, "saveGame.json") print(saveGame[1][1].type) saveGame = utility.loadTable("saveGame.json") print(saveGame["1"][1].type)

This doesn’t:

 local saveGame = {} saveGame = mapData print(saveGame[1][1].type) utility.saveTable(saveGame, "saveGame.json") print(saveGame[1][1].type) saveGame = utility.loadTable("saveGame.json") print(saveGame[1][1].type) --Crash here - "attempt to index field '?' (a nil value)"

The load/save functions I’m using are Rob Miracle’s utilities from here.

function M.saveTable(t, filename) local path = system.pathForFile( filename, system.DocumentsDirectory) local file = io.open(path, "w") if file then local contents = json.encode(t) file:write( contents ) io.close( file ) return true else return false end end function M.loadTable(filename) local path = system.pathForFile( filename, system.DocumentsDirectory) local contents = "" local myTable = {} local file = io.open( path, "r" ) if file then local contents = file:read( "\*a" ) myTable = json.decode(contents); io.close( file ) return myTable end print(filename, "file not found") return nil end

Any ideas for how to fix this? I’m considering saving/loading each array of the 2D array separately, or creating a custom parser for the corrupted JSON, but I figure there must be a better solution.

It’s because “1” and 1 are different types in Lua, and it looks like something in the json encode/decode process is causing the numeric key to be saved as a string key.  

I’ve run into this problem before, and made this function to fix it:

function utility.toNumericKeys(t) local t2 = {} for k,v in pairs(t) do if tonumber(k) and type(k) ~= "number" then t2[tonumber(k)] = v else t2[k] = v end end return t2 end

It checks for any keys in a table which should be numeric but are currently strings, and converts them accordingly. Try using it during the load process like so:

saveGame = utility.toNumericKeys(utility.loadTable("saveGame.json"))

The one place where this would cause problems is if you want the key to be a string key but the string only contains numbers e.g. myTable[“123”] = “someValue”

Thanks Alan. I dropped your function in and it works perfectly. 

Glad I’m not the only one who’s run into this. Have a good weekend.

Just a quick note for anyone still looking at this or wondering what happened.

You can’t mix string indexes and numeric indexes when JSON encoding.

That is, you can, but when you JSON decode all of the indexes will be strings:

i.e. This

local oops = {} oops[1] = "hi" oops[2] = "there" oops["name"]= "bob" local tmp = json.encode( oops)

becomes this:

local oops = json.decode( tmp ) -- Now indexed like this: --[[oops["1"] = "hi" oops["2"] = "there" oops["name"] = "bob" --]]

@roaringgamer Now that you mention it, that was exactly what I had done previously. I’d forgotten that was the cause of this problem, thanks for the reminder.  :slight_smile:

It’s because “1” and 1 are different types in Lua, and it looks like something in the json encode/decode process is causing the numeric key to be saved as a string key.  

I’ve run into this problem before, and made this function to fix it:

function utility.toNumericKeys(t) local t2 = {} for k,v in pairs(t) do if tonumber(k) and type(k) ~= "number" then t2[tonumber(k)] = v else t2[k] = v end end return t2 end

It checks for any keys in a table which should be numeric but are currently strings, and converts them accordingly. Try using it during the load process like so:

saveGame = utility.toNumericKeys(utility.loadTable("saveGame.json"))

The one place where this would cause problems is if you want the key to be a string key but the string only contains numbers e.g. myTable[“123”] = “someValue”

Thanks Alan. I dropped your function in and it works perfectly. 

Glad I’m not the only one who’s run into this. Have a good weekend.

Just a quick note for anyone still looking at this or wondering what happened.

You can’t mix string indexes and numeric indexes when JSON encoding.

That is, you can, but when you JSON decode all of the indexes will be strings:

i.e. This

local oops = {} oops[1] = "hi" oops[2] = "there" oops["name"]= "bob" local tmp = json.encode( oops)

becomes this:

local oops = json.decode( tmp ) -- Now indexed like this: --[[oops["1"] = "hi" oops["2"] = "there" oops["name"] = "bob" --]]

@roaringgamer Now that you mention it, that was exactly what I had done previously. I’d forgotten that was the cause of this problem, thanks for the reminder.  :slight_smile: