Posted on by

This week, I’m going to cover a web technology that has been dubbed “the fat-free alternative to XML” — JSON. While XML is great, there are a few problems with it…

  • Because XML tags on the same level can have the same name, accessing XML data from Corona can get to be really confusing if you don’t pre-localize all the children in each level of the node hierarchy with your own naming convention (very confusing).
  • It’s really only useful if you need to read-in data from an XML file. If you need to package up data into an XML file from your Corona app, you’re in for a rough ride.

Thankfully, using JSON in a Corona app is a lot more practical and the functions are integrated into the Corona core.

Getting Started

Before using JSON in your Corona app, you’ll probably want to learn a little more about it. Apart from a few small syntax differences, JSON is very similar in structure to Lua tables.

Here’s a file, sample.json, that we’ll be using throughout this tutorial:

{
    "name": "Jack (\"Bee\") Nimble", 
    "format": {
        "shape":       "rect", 
        "width":      1920, 
        "height":     1080, 
        "interlace":  false, 
        "framerate": 24
    }
}

NOTE: As you can see from the sample above, the main difference between JSON and a Lua table is the use of colons “:” instead of the equals sign “=” for assignment.

Decoding the JSON

When you have a JSON string, you need to “decode” it, which is another way of saying, “take my JSON and convert it into a Lua table so I can use it”.

Here’s how to use the function I provided above to load a (local) JSON file and
“decode” it into a Lua table:

local json = require "json"
local t = json.decode( jsonFile( "sample.json" ) )

The first line will make Corona’s JSON functions available via the “json” namespace. Visit the JSON documentation to see all of Corona’s JSON functions.

The next line will call json.decode() and load the local JSON file using the function I provided earlier. The json.decode() function takes one argument, which is a JSON-encoded string.

Here is the contents of table t from the example above:

{
	name = "Jack (\"Bee\") Nimble",
	format = {
		shape = "rect",
		width = 1920,
		height = 1080,
		interlace = false,
		framerate = 24
	}
}

Now you can access the JSON data in the exact way as you access data with any other Lua table! See? Much easier than XML.

Encoding an Existing Table

What if you have an existing Lua table and you need to turn it into a JSON string? This is also known as “serializing” a table, and can be done with just one line of code:

local jsonString = json.encode( myLuaTable )

This is useful if you want to either take the string and save it into a file for later loading (e.g. “decoding”), or if you need to send a lot of data over the web and want to do it as easy as possible.

To work with remote JSON files, the process is exactly the same, except that you’ll need to download the file first.

Because JSON itself is very similar to Lua tables, and the fact that we’ve integrated the JSON library into the Corona core for easier access makes it obvious that we recommend using JSON over XML, but there may be cases where you still need to use XML.


Posted by . Thanks for reading...

24 Responses to “Tutorial: Basic JSON Usage”

  1. Mo

    Hi Jon

    Another hit! Thank you so much for doing this!.

    Quick question. I am using the great propertyBag at this time to save my game settings between play. Would Your way using Json would work for that? For instance say I have a gameSettings table like

    GameSetting = {
    soundOn = true,
    MusicOn= true,
    Score = 1000,
    Sprite1.x =400,
    sprite1.y = 100,
    sprite2.x = 500,
    Sprite3.y = 300
    }

    Of course this table will need to be save and reloaded when a game restart. Is Using Json in term of speed a real problem? Compare to Propertybag or simple file I/O ?

    Thanks again Jon. It is amazing how you can explain pretty difficult concept in such simple way.

    Thanks.

    Mo

    Reply
  2. Neovive

    Is it possible to read the JSON file directly over the network without downloading the file first?

    Reply
  3. Jonathan Beebe

    @MO: I think JSON would be perfect for you, for saving multiple settings. I don’t think you need to worry about performance if you’re just loading the settings once and going from there. If you were to load a JSON file on every frame, or something like that, then that would be a different story.

    @Neovive: When you “read” something over a network, such as viewing a webpage on the internet, it is actually getting downloaded to your computer. What you could do is save the file to system.TemporaryDirectory and then read it from there. When the user closes the app, everything in that directory is erased (so essentially, it’s almost the same as reading over the network). Here’s the network.download documentation: http://developer.anscamobile.com/reference/index/networkdownload

    Reply
  4. Mo

    Cool Jon. Thank you. I am assuming but I need to ask: any table that contains number (rather than a string) will be decoded as a number and no need to use tonnumber() correct? Same question for bolean?

    Thanks again. Json encoding/decoding will greatly simplify saving game settings since it seems that you can save/load a full table of game settings!

    Mo

    Reply
  5. madmusic6

    Hi all

    I can’t get this to work. I put the sample.json in the resources then
    put in the code for jsonFile then
    require “json”
    local t = json.decode( jsonFile( “sample.json” ) )
    “Now you can access the JSON data in the exact way as you access data with any other Lua table!”
    This I can’t get to work. I’ve tried loads of things like: print (#t) >— 0
    I’ve tried loads of ways, like the explode function etc. Saving it as a string etc.
    Has anyone got an example that uses this?
    I wanted to have a table of game settings, encode the table, save the table. Then when the game is relaunched, it can load in the table and access it. Just a small example would be great!
    Thanks

    Reply
  6. Mo

    Hi Jon.

    Someone yesterday at the Corona meetup told me that you were in the house. I really was hoping to meet you to personally thank you for the great tutorials you put up here and the always to the point answers to people. I hope to meet you at the next Meetup.

    THANK YOU!

    Mohamed,

    Reply
  7. Rob

    Can the corona json library serialise tables within tables and ‘objects’ using the metatables technique?

    Reply
  8. Ivan.li

    Hi,I have a problem about this.

    i can run it ok on corona sdk 894,but canot run it on sdk 971,the same code.

    the key code is *** local levelData = json.decode(jsonFile( “level_data.json” )) ***

    if you have time,send email to me. Ivan.li@creata.com
    thank you very much.

    Reply
  9. Ivan.li

    Hi, Jonathan
    i have a problem about json.decode,
    i can tun it ok on sdk 892,but cannot run it on sdk971.the same code.

    the key code is ” local levelData = json.decode(jsonFile( “level_data.json” )) ”
    it says levelData is a nil value on Corona SDK971.

    if you have time,please send email to me,Ivan.Li@creata.com
    thank you very much..

    Reply
    • Rob Miracle

      Lua has two modes for tables, one is with a numeric index which is like an Array, and the other is a key-value pair “hash” type of data structure. If you use a numeric array, the order of the cells should be preserved (the actual order in the file doesn’t matter). For the hash mode, the order of the table entries isn’t guaranteed.

      Reply
      • imn

        hello,
        I am making a game that loading data from file
        I used two ways ,worked on emulator but not on android phone
        one using text file and choosing randomly from it
        and another json file and loading it to table then choosing randomly from it
        I tried many times and it doesn’t work
        the last time I tried to save the table into file then load it and choose it worked
        but that not what I want I just want it to load
        thanks

        Reply
  10. Abdulhameed

    i’ve tried the above code
    —main.lua—
    local json = require “json”
    local t = json.decode( jsonFile( “sample.json” ) )
    print(t.name)
    print(t.format.shape)

    —sample.json—
    {
    name = “Jack (\”Bee\”) Nimble”,
    format = {
    shape = “rect”,
    width = 1920,
    height = 1080,
    interlace = false,
    framerate = 24
    }
    }
    —–
    but it didn’t work
    throwing this error
    Attempt to call global ‘jsonFile’ (a nil value)
    —————

    then i’ve tried this code
    —-main.lua—–
    local json = require “json”

    function jsonFile( filename )
    — set default base dir if none specified
    local base = system.ResourceDirectory

    — create a file path for corona i/o
    local path = system.pathForFile( filename, base )

    — will hold contents of file
    local contents

    — io.open opens a file at path. returns nil if no file found
    local file = io.open( path, “r” )
    if file then
    — read all contents of file into a string
    contents = file:read( “*a” )
    io.close( file ) — close the file after using it
    –return decoded json string
    return json.decode( contents )
    else
    –or return nil if file didn’t ex
    return nil
    end
    end
    local t = jsonFile( “sample.json” )
    print(t.name)
    print(t.format.shape)

    but it didn’t work
    throwing this error
    Attempt to index local ‘t’ (a nil value)

    please help

    Reply
  11. Felix

    I also can not get this code running :(
    One thing that seems to be wrong is here:

    local t = json.decode( jsonFile( “sample.json” ) )

    if I put it this way:

    local t = json.decode( “sample.json” )

    it starts but I dont have any values in t.
    I get the same errors like Abdulhameed.

    I am very new to lua and corona and would be happy for some solution. Even if it is something obvious, that I am missing.
    Thx,
    Felix

    Reply
  12. eddie

    i cant get this to work and its exactly what i need to do. i just get an error… does anyone know of another example of this? or have the solution for it? i see that others are having the same problems.

    Reply
  13. Tijmen

    I’m having exactly the same problem as Abdulhammeed and Felix

    Attempt to call global ‘jsonFile’ (a nil value)

    I’ve downloaded a JSON file which resides in my project Sanbox in tmp.
    I would like to json.decode.

    I thought jsonFile is a function which resides in require(“json”)
    apparently not.

    Anybody know how to fix this? Thnx

    Reply
    • Tijmen

      It seems that since I use a new build that json.encode returns nil

      I added this jsonFile function

      function jsonFile( filename )
      — set default base dir if none specified
      local base = system.TemporaryDirectory

      — create a file path for corona i/o
      local path = system.pathForFile( filename, base )
      print(path)
      — will hold contents of file
      local contents

      — io.open opens a file at path. returns nil if no file found
      local file = io.open( path, “r” )
      if file then
      — read all contents of file into a string
      contents = file:read( “*a” )
      io.close( file ) — close the file after using it
      –return string

      return contents
      else
      –or return nil if file didn’t ex
      return nil
      end
      end

      It know seems that json.encode return nil since I changed to a new build. The old build read my JSON perfectly.
      Is my JSON suddenly not correct anymore? What could have happened?

      Reply
  14. Thomas

    Very useful, thanks.
    But, I get a crash when calling json.encode() for my table.
    It works if I create a manual table or remove the ‘bad’ subtable from the main table.

    The table is kind of big with quite some nested tables, but I would not expect this to be a problem.
    Are there any known issues with json.encode and complex tables?

    regards,
    Thomas

    Reply
  15. Jason

    I do not usually comment on things like this, but this page should be taken down or updated. The code does not work, and the Corona functions are not current. Most of the Corona resources like this are phenomenal (awesome overall job guys!), but this page became very frustrating as I had to figure out pretty much all of this functionality it promised from scratch on my own.

    Reply

Leave a Reply

  • (Will Not Be Published)