Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Table of Contents
* [encode_number_precision](#encode_number_precision)
* [encode_escape_forward_slash](#encode_escape_forward_slash)
* [encode_skip_unsupported_value_types](#encode_skip_unsupported_value_types)
* [encode_indent](#encode_indent)
* [decode_array_with_array_mt](#decode_array_with_array_mt)

Description
Expand Down Expand Up @@ -201,6 +202,31 @@ This will generate:

[Back to TOC](#table-of-contents)

encode_indent
----------------------------
**syntax:** `cjson.encode_indent(indent)`

If non-empty string provided, JSON values encoded by `cjson.encode()` will be
formatted in a human-readable way, using `indent` for indentation
at each nesting level. Also enables newlines and a space after colons.

Example:

```lua
local cjson = require "cjson"

cjson.encode_indent(" ")
print(cjson.encode({ a = 1, b = { c = 2 } }))
-- {
-- "a": 1,
-- "b": {
-- "c": 2
-- }
-- }
```

[Back to TOC](#table-of-contents)

decode_array_with_array_mt
--------------------------
**syntax:** `cjson.decode_array_with_array_mt(enabled)`
Expand Down
56 changes: 56 additions & 0 deletions lua_cjson.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
#define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0
#define DEFAULT_ENCODE_INDENT NULL

#ifdef DISABLE_INVALID_NUMBERS
#undef DEFAULT_DECODE_INVALID_NUMBERS
Expand Down Expand Up @@ -172,6 +173,7 @@ typedef struct {
int encode_keep_buffer;
int encode_empty_table_as_object;
int encode_escape_forward_slash;
const char *encode_indent;

int decode_invalid_numbers;
int decode_max_depth;
Expand Down Expand Up @@ -310,6 +312,18 @@ static int json_enum_option(lua_State *l, int optindex, int *setting,
return 1;
}

/* Process string option for a configuration function */
static int json_string_option(lua_State *l, int optindex, const char **setting)
{
if (!lua_isnil(l, optindex)) {
const char *value = luaL_checkstring(l, optindex);
*setting = value;
}

lua_pushstring(l, *setting ? *setting : "");
return 1;
}

/* Configures handling of extremely sparse arrays:
* convert: Convert extremely sparse arrays into objects? Otherwise error.
* ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio
Expand Down Expand Up @@ -400,6 +414,18 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
return 1;
}

/* Configure how to indent output */
static int json_cfg_encode_indent(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 1);

json_string_option(l, 1, &cfg->encode_indent);
/* simplify further checking */
if (cfg->encode_indent[0] == '\0') cfg->encode_indent = NULL;

return 1;
}

#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV)
void json_verify_invalid_number_setting(lua_State *l, int *setting)
{
Expand Down Expand Up @@ -491,6 +517,7 @@ static void json_create_config(lua_State *l)
cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT;
cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH;
cfg->encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES;
cfg->encode_indent = DEFAULT_ENCODE_INDENT;

#if DEFAULT_ENCODE_KEEP_BUFFER > 0
strbuf_init(&cfg->encode_buf, 0);
Expand Down Expand Up @@ -660,6 +687,13 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
static int json_append_data(lua_State *l, json_config_t *cfg,
int current_depth, strbuf_t *json);

static void json_append_newline_and_indent(strbuf_t *json, json_config_t *cfg, int depth)
{
strbuf_append_char(json, '\n');
for (int i = 0; i < depth; i++)
strbuf_append_string(json, cfg->encode_indent);
}

/* json_append_array args:
* - lua_State
* - JSON strbuf
Expand All @@ -668,15 +702,21 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
strbuf_t *json, int array_length, int raw)
{
int comma, i, json_pos, err;
int has_items = 0;

strbuf_append_char(json, '[');

comma = 0;
for (i = 1; i <= array_length; i++) {
has_items = 1;

json_pos = strbuf_length(json);
if (comma++ > 0)
strbuf_append_char(json, ',');

if (cfg->encode_indent)
json_append_newline_and_indent(json, cfg, current_depth);

if (raw) {
lua_rawgeti(l, -1, i);
} else {
Expand All @@ -698,6 +738,9 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
lua_pop(l, 1);
}

if (has_items && cfg->encode_indent)
json_append_newline_and_indent(json, cfg, current_depth-1);

strbuf_append_char(json, ']');
}

Expand Down Expand Up @@ -752,6 +795,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
int current_depth, strbuf_t *json)
{
int comma, keytype, json_pos, err;
int has_items = 0;

/* Object */
strbuf_append_char(json, '{');
Expand All @@ -760,10 +804,15 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
/* table, startkey */
comma = 0;
while (lua_next(l, -2) != 0) {
has_items = 1;

json_pos = strbuf_length(json);
if (comma++ > 0)
strbuf_append_char(json, ',');

if (cfg->encode_indent)
json_append_newline_and_indent(json, cfg, current_depth);

/* table, key, value */
keytype = lua_type(l, -2);
if (keytype == LUA_TNUMBER) {
Expand All @@ -778,6 +827,9 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
"table key must be a number or string");
/* never returns */
}
if (cfg->encode_indent)
strbuf_append_char(json, ' ');


/* table, key, value */
err = json_append_data(l, cfg, current_depth, json);
Expand All @@ -792,6 +844,9 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
/* table, key */
}

if (has_items && cfg->encode_indent)
json_append_newline_and_indent(json, cfg, current_depth-1);

strbuf_append_char(json, '}');
}

Expand Down Expand Up @@ -1571,6 +1626,7 @@ static int lua_cjson_new(lua_State *l)
{ "decode_invalid_numbers", json_cfg_decode_invalid_numbers },
{ "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash },
{ "encode_skip_unsupported_value_types", json_cfg_encode_skip_unsupported_value_types },
{ "encode_indent", json_cfg_encode_indent },
{ "new", lua_cjson_new },
{ NULL, NULL }
};
Expand Down
38 changes: 38 additions & 0 deletions tests/test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,44 @@ local cjson_tests = {
json.decode, { [["\uDB00\uD"]] },
false, { "Expected value but found invalid unicode escape code at character 2" } },

-- Test indenting
{ 'Set encode_indent(" ")',
json.encode_indent, { " " }, true, { " " } },
{ "Encode object with indenting",
json.encode, { { a = "a", b = "b" } },
true, {
util.one_of {
'{\n "a": "a",\n "b": "b"\n}',
'{\n "b": "b",\n "a": "a"\n}',
}
} },
{ "Encode empty object with indenting",
json.encode, { { } }, true, { '{}' } },
{ "Encode nested object with indenting",
json.encode, { { a = { b = 1 } } },
true, { '{\n "a": {\n "b": 1\n }\n}' } },
{ "Encode array with indenting",
json.encode, { { 1, 2, 3 } },
true, { '[\n 1,\n 2,\n 3\n]' } },
{ "Encode empty array with indenting",
json.encode, { json.empty_array }, true, { '[]' } },
{ "Encode nested arrays with indenting",
json.encode, { { { 1, 2 }, { 3, 4 } } },
true, { '[\n [\n 1,\n 2\n ],\n [\n 3,\n 4\n ]\n]' } },
{ "Encode array of objects with indenting",
json.encode, { { { a = "a" }, { b = "b" } } },
true, { '[\n {\n "a": "a"\n },\n {\n "b": "b"\n }\n]' } },
{ 'Set encode_indent("abc")',
json.encode_indent, { "abc" }, true, { "abc" } },
{ "Encode object with non-whitespace indenting",
json.encode, { { a = { b = 1 } } },
true, { '{\nabc"a": {\nabcabc"b": 1\nabc}\n}' } },
{ 'Set encode_indent("")',
json.encode_indent, { "" }, true, { "" } },
{ "Encode array of objects with empty indenting",
json.encode, { { { a = "a" }, { b = "b" } } },
true, { '[{"a":"a"},{"b":"b"}]' } },

-- Test locale support
--
-- The standard Lua interpreter is ANSI C online doesn't support locales
Expand Down