JSON (JavaScript Object Notation) is a format for serializing data based on the format for JavaScript data structures. For more informations about JSON in general see http://www.json.org/ and [the Wikipedia article on JSON].
A comparison of JSON modules
Since some modules were only called "json" initials were added to create unique names:
- cmj-JSON4Lua (version 0.9.5):
- dkjson (version 2.1):
- Fleece (version 0.3.1):
- jf-JSON (version 4)
- Lua-Yajl (version 2.0):
- http://github.com/brimworks/lua-yajl
- C-module, requires [Yajl] (used version 2.0.1 at the time of testing).
- Can be used for streaming by writing handlers that deal with JSON on the fly.
- Lua-Yail uses the MIT/X11 license, Yajl itself uses the ISC license.
- mp-CJSON / Lua CJSON (version 1.0.2):
- nm-luajsonlib / LuaJSON C Library (version 1.2):
- sb-Json (2007 version):
- th-LuaJSON (version 1.2.1):
This comparison was initially started by DavidKolf, author of dkjson, so it might be biased.
For comparison the default options were used. I (DavidKolf) expect a module to be JSON-compliant with the default settings. An exception was made for Fleece, most of this comparison wouldn't make sense for anything other than the option "E4".
The [feature/compliance tests] and the [speed test] can be found in the repository for dkjson.
Further JSON implementations
JSON implementations that are not included in this comparison (yet):
Major bugs
- Fleece:
- Encoding +/-math.huge (Infinity) freezes the program (endless loop).
- Encoding UTF-8 codes for codepoints >= 128 cancels further output.
- Reference cycles cause segmentation faults.
- nm-luajsonlib:
- Reference cycles cause segmentation faults.
Escaped characters when encoding
According to [RFC 4627] all characters in the range 0 to 31 plus \\ and \" have to be escaped. However, it is recommended to also escape the Unicode characters U+2028 and U+2029 in order to [generate valid JavaScript as well]. The [original JSON implementation] went even further and escaped most control characters and undefined codepoints in the BMP.
- cmj-JSON4Lua: \\, \", \n, \t plus \' which isn't a valid JSON escape code. JSON files containing this would not load in the original implementation.
- dkjson: All characters in the range 0 to 31, \\, \", 127 and the other Unicode control characters that are escaped by the original implementation.
- Fleece (with the E4 option): All characters in the range 0 to 31, \\, \", \/, 127. All characters above 127 cancel further output.
- jf-JSON: All characters in the range 0 to 31, \\, \".
- Lua-Yajl: All characters in the range 0 to 31, \\, \".
- mp-CJSON: All characters in the range 0 to 31, \\, \", \/, 127.
- nm-luajsonlib: All characters in the range 0 to 31, \\, \". All characters above 127 insert broken escapes.
- sb-Json: All characters in the range 0 to 31, \\, \", \/, 127.
- th-LuaJSON: All characters in the range 0 to 31, \\, \", \/. \u000b (11) is escaped as \v, which isn't valid JSON code and would not load in the original implementation.
Support for unicode escape sequences \uXXXX when decoding
- cmj-JSON4Lua: no support
- dkjson: full support from 0 to 0x10ffff, correctly converts [UTF-16] surrogate pairs to [UTF-8].
- jf-JSON: full support from 0 to 0x10ffff, correctly converts UTF-16 surrogate pairs to UTF-8.
- Lua-Yajl: full support from 0 to 0x10ffff, correctly converts UTF-16 surrogate pairs to UTF-8.
- mp-CJSON: full support from 0 to 0x10ffff, correctly converts UTF-16 surrogate pairs to UTF-8.
- nm-luajsonlib: support in the range 0 to 0xffff, but doesn't convert UTF-16 surrogate pairs, so 0x10000 to 0x10ffff would be encoded as [CESU-8] instead of UTF-8.
- sb-Json: support in the range 0 to 0xff, creates 8-bit encoding (latin1 or similar).
- th-LuaJSON: support in the range 0 to 0xffff, but doesn't convert UTF-16 surrogate pairs, so 0x10000 to 0x10ffff would be encoded as CESU-8 instead of UTF-8.
Other bugs
- cmj-JSON4Lua:
- Decoding an empty array raises an error.
- Decoding a number with a plus sign in the exponential part raises an error ("1e+2").
Edge cases for encoding
JSON cannot represent every Lua structure. This section lists the behavior for some cases. You shouldn't feed any data like this to any JSON module but it's still interesting how the modules handle this. A module must not hang or crash and it should not produce invalid JSON output. (Raising Lua errors is a valid way to deal with such data).
Mixed tables
{[1] = 1, a = 2}
- cmj-JSON4Lua: integer keys are converted to strings.
- dkjson: integer keys are converted to strings.
- Fleece: integer keys are encoded as numbers (invalid JSON).
- jf-JSON: raises an error.
- Lua-Yajl: integer keys are converted to strings.
- mp-CJSON: integer keys are converted to strings.
- nm-luajsonlib: encoded as an array including only the integer keys up to the Lua length (#-operator).
- sb-Json: integer keys are converted to strings.
- th-LuaJSON: integer keys are converted to strings.
Sparse arrays
{[1000] = "test"}
- cmj-JSON4Lua: encoded as a big array.
- dkjson: encoded as an object with string keys.
- Fleece: encoded as an object with integer keys (invalid JSON).
- jf-JSON: encoded as a a big array.
- Lua-Yajl: encoded as a big array.
- mp-CJSON: raises an error for excessively sparse arrays and converts other sparse arrays to an object with string keys. Behaviour is runtime configurable.
- nm-luajsonlib: if the Lua length is 0, the table is encoded as an object with integer keys (invalid JSON). Otherwise only elements up to Lua length (#-operator) are encoded.
- sb-Json: encoded as a big array.
- th-LuaJSON: encoded as an object with string keys.
Handling of NaN and Inf
nan = math.huge * 0
inf = math.huge
- cmj-JSON4Lua: raw tostring() output (invalid JSON).
- dkjson: 'null' (like in the original JSON-implementation).
- Fleece: NaN is 0.0000, freezes on +/-Inf.
- jf-JSON: NaN is 'null', Inf is 1e+9999 (the encode_pretty function still outputs raw tostring()).
- Lua-Yajl: NaN is -0, Inf is 1e+666.
- mp-CJSON: raises invalid JSON error by default, but runtime configurable ('null' or Nan/Inf).
- nm-luajsonlib: 'null' (like in the original JSON-implementation).
- sb-Json: raw tostring() output (invalid JSON).
- th-LuaJSON: JavaScript constants: NaN is 'NaN', Inf is 'Infinity' (this is valid JavaScript, but invalid JSON).
Protection against reference cycles when encoding
a = {}
a.a = a
- cmj-JSON4Lua: no, endless loop.
- dkjson: yes, raises an error.
- Fleece: no, segmentation fault.
- jf-JSON: yes, raises an error.
- Lua-Yajl: yes, raises an error.
- mp-CJSON: yes, raises an error. limit is runtime configurable.
- nm-luajsonlib: no, segmentation fault.
- sb-Json: no, endless loop.
- th-LuaJSON: yes, raises an error.
Handling of empty arrays
local json_str = "{'items':[],'properties':{}}"
assert(json_str == json.encode(json.decode(json_str))
- cmj-JSON4Lua: does not work correctly (empty arrays are encoded as simple empty tables)
- dkjson: works correctly (uses a "type field" on the metatable)
- Fleece: not tested
- jf-JSON: not tested
- Lua-Yajl: does not work correctly (empty tables are encoded as empty arrays, always)
- mp-CJSON: does not work correctly (empty tables are encoded as empty objects, always)
- nm-luajsonlib: not tested
- sb-Json: not tested
- th-LuaJSON: (v1.3) works (assigns a special metatable to arrays)
Encoding speed
- cmj-JSON4Lua: 12s
- dkjson: 16s
- Fleece: 0.5s
- jf-JSON: 20s
- Lua-Yajl: 5.9s
- mp-CJSON: 1s
- nm-luajsonlib: 5s
- sb-Json: 22s
- th-LuaJSON: 30s
Decoding speed
- cmj-JSON4Lua: 47s
- dkjson: 28s without LPeg, 8s with LPeg
- jf-JSON: 30s
- Lua-Yajl: 4s
- mp-CJSON: 2s
- nm-luajsonlib: 3s
- sb-Json: 122s
- th-LuaJSON: 9s (version 1.2.2 takes 347s)
For the speed tests a table was encoded or decoded 100000 times. The values are not precise and might differ when using other data, but they should give a general orientation.
See Also
- [JSONRPC4Lua] (5.0/5.1) - JSON-RPC-over-http client (or server in a CGILua environment) for Lua.
RecentChanges · preferences
edit · history
Last edited August 26, 2014 12:43 pm GMT (diff)