Skip to content

Commit 1b17610

Browse files
committed
feature: add option to indent encoded output
1 parent 91ca29d commit 1b17610

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Table of Contents
1616
* [encode_number_precision](#encode_number_precision)
1717
* [encode_escape_forward_slash](#encode_escape_forward_slash)
1818
* [encode_skip_unsupported_value_types](#encode_skip_unsupported_value_types)
19+
* [encode_indent](#encode_indent)
1920
* [decode_array_with_array_mt](#decode_array_with_array_mt)
2021

2122
Description
@@ -201,6 +202,31 @@ This will generate:
201202

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

205+
encode_indent
206+
----------------------------
207+
**syntax:** `cjson.encode_indent(indent)`
208+
209+
If non-empty string provided, JSON values encoded by `cjson.encode()` will be
210+
formatted in a human-readable way, using `indent` for indentation
211+
at each nesting level. Also enables newlines and a space after colons.
212+
213+
Example:
214+
215+
```lua
216+
local cjson = require "cjson"
217+
218+
cjson.encode_indent(" ")
219+
print(cjson.encode({ a = 1, b = { c = 2 } }))
220+
-- {
221+
-- "a": 1,
222+
-- "b": {
223+
-- "c": 2
224+
-- }
225+
-- }
226+
```
227+
228+
[Back to TOC](#table-of-contents)
229+
204230
decode_array_with_array_mt
205231
--------------------------
206232
**syntax:** `cjson.decode_array_with_array_mt(enabled)`

lua_cjson.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
9292
#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
9393
#define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0
94+
#define DEFAULT_ENCODE_INDENT NULL
9495

9596
#ifdef DISABLE_INVALID_NUMBERS
9697
#undef DEFAULT_DECODE_INVALID_NUMBERS
@@ -172,6 +173,7 @@ typedef struct {
172173
int encode_keep_buffer;
173174
int encode_empty_table_as_object;
174175
int encode_escape_forward_slash;
176+
const char *encode_indent;
175177

176178
int decode_invalid_numbers;
177179
int decode_max_depth;
@@ -310,6 +312,18 @@ static int json_enum_option(lua_State *l, int optindex, int *setting,
310312
return 1;
311313
}
312314

315+
/* Process string option for a configuration function */
316+
static int json_string_option(lua_State *l, int optindex, const char **setting)
317+
{
318+
if (!lua_isnil(l, optindex)) {
319+
const char *value = luaL_checkstring(l, optindex);
320+
*setting = value;
321+
}
322+
323+
lua_pushstring(l, *setting ? *setting : "");
324+
return 1;
325+
}
326+
313327
/* Configures handling of extremely sparse arrays:
314328
* convert: Convert extremely sparse arrays into objects? Otherwise error.
315329
* ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio
@@ -400,6 +414,18 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
400414
return 1;
401415
}
402416

417+
/* Configure how to indent output */
418+
static int json_cfg_encode_indent(lua_State *l)
419+
{
420+
json_config_t *cfg = json_arg_init(l, 1);
421+
422+
json_string_option(l, 1, &cfg->encode_indent);
423+
/* simplify further checking */
424+
if (cfg->encode_indent[0] == '\0') cfg->encode_indent = NULL;
425+
426+
return 1;
427+
}
428+
403429
#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV)
404430
void json_verify_invalid_number_setting(lua_State *l, int *setting)
405431
{
@@ -491,6 +517,7 @@ static void json_create_config(lua_State *l)
491517
cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT;
492518
cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH;
493519
cfg->encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES;
520+
cfg->encode_indent = DEFAULT_ENCODE_INDENT;
494521

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

690+
static void json_append_newline_and_indent(strbuf_t *json, json_config_t *cfg, int depth)
691+
{
692+
strbuf_append_char(json, '\n');
693+
for (int i = 0; i < depth; i++)
694+
strbuf_append_string(json, cfg->encode_indent);
695+
}
696+
663697
/* json_append_array args:
664698
* - lua_State
665699
* - JSON strbuf
@@ -668,15 +702,21 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
668702
strbuf_t *json, int array_length, int raw)
669703
{
670704
int comma, i, json_pos, err;
705+
int has_items = 0;
671706

672707
strbuf_append_char(json, '[');
673708

674709
comma = 0;
675710
for (i = 1; i <= array_length; i++) {
711+
has_items = 1;
712+
676713
json_pos = strbuf_length(json);
677714
if (comma++ > 0)
678715
strbuf_append_char(json, ',');
679716

717+
if (cfg->encode_indent)
718+
json_append_newline_and_indent(json, cfg, current_depth);
719+
680720
if (raw) {
681721
lua_rawgeti(l, -1, i);
682722
} else {
@@ -698,6 +738,9 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
698738
lua_pop(l, 1);
699739
}
700740

741+
if (has_items && cfg->encode_indent)
742+
json_append_newline_and_indent(json, cfg, current_depth-1);
743+
701744
strbuf_append_char(json, ']');
702745
}
703746

@@ -752,6 +795,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
752795
int current_depth, strbuf_t *json)
753796
{
754797
int comma, keytype, json_pos, err;
798+
int has_items = 0;
755799

756800
/* Object */
757801
strbuf_append_char(json, '{');
@@ -760,10 +804,15 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
760804
/* table, startkey */
761805
comma = 0;
762806
while (lua_next(l, -2) != 0) {
807+
has_items = 1;
808+
763809
json_pos = strbuf_length(json);
764810
if (comma++ > 0)
765811
strbuf_append_char(json, ',');
766812

813+
if (cfg->encode_indent)
814+
json_append_newline_and_indent(json, cfg, current_depth);
815+
767816
/* table, key, value */
768817
keytype = lua_type(l, -2);
769818
if (keytype == LUA_TNUMBER) {
@@ -778,6 +827,9 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
778827
"table key must be a number or string");
779828
/* never returns */
780829
}
830+
if (cfg->encode_indent)
831+
strbuf_append_char(json, ' ');
832+
781833

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

847+
if (has_items && cfg->encode_indent)
848+
json_append_newline_and_indent(json, cfg, current_depth-1);
849+
795850
strbuf_append_char(json, '}');
796851
}
797852

@@ -1571,6 +1626,7 @@ static int lua_cjson_new(lua_State *l)
15711626
{ "decode_invalid_numbers", json_cfg_decode_invalid_numbers },
15721627
{ "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash },
15731628
{ "encode_skip_unsupported_value_types", json_cfg_encode_skip_unsupported_value_types },
1629+
{ "encode_indent", json_cfg_encode_indent },
15741630
{ "new", lua_cjson_new },
15751631
{ NULL, NULL }
15761632
};

tests/test.lua

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,44 @@ local cjson_tests = {
333333
json.decode, { [["\uDB00\uD"]] },
334334
false, { "Expected value but found invalid unicode escape code at character 2" } },
335335

336+
-- Test indenting
337+
{ 'Set encode_indent(" ")',
338+
json.encode_indent, { " " }, true, { " " } },
339+
{ "Encode object with indenting",
340+
json.encode, { { a = "a", b = "b" } },
341+
true, {
342+
util.one_of {
343+
'{\n "a": "a",\n "b": "b"\n}',
344+
'{\n "b": "b",\n "a": "a"\n}',
345+
}
346+
} },
347+
{ "Encode empty object with indenting",
348+
json.encode, { { } }, true, { '{}' } },
349+
{ "Encode nested object with indenting",
350+
json.encode, { { a = { b = 1 } } },
351+
true, { '{\n "a": {\n "b": 1\n }\n}' } },
352+
{ "Encode array with indenting",
353+
json.encode, { { 1, 2, 3 } },
354+
true, { '[\n 1,\n 2,\n 3\n]' } },
355+
{ "Encode empty array with indenting",
356+
json.encode, { json.empty_array }, true, { '[]' } },
357+
{ "Encode nested arrays with indenting",
358+
json.encode, { { { 1, 2 }, { 3, 4 } } },
359+
true, { '[\n [\n 1,\n 2\n ],\n [\n 3,\n 4\n ]\n]' } },
360+
{ "Encode array of objects with indenting",
361+
json.encode, { { { a = "a" }, { b = "b" } } },
362+
true, { '[\n {\n "a": "a"\n },\n {\n "b": "b"\n }\n]' } },
363+
{ 'Set encode_indent("abc")',
364+
json.encode_indent, { "abc" }, true, { "abc" } },
365+
{ "Encode object with non-whitespace indenting",
366+
json.encode, { { a = { b = 1 } } },
367+
true, { '{\nabc"a": {\nabcabc"b": 1\nabc}\n}' } },
368+
{ 'Set encode_indent("")',
369+
json.encode_indent, { "" }, true, { "" } },
370+
{ "Encode array of objects with empty indenting",
371+
json.encode, { { { a = "a" }, { b = "b" } } },
372+
true, { '[{"a":"a"},{"b":"b"}]' } },
373+
336374
-- Test locale support
337375
--
338376
-- The standard Lua interpreter is ANSI C online doesn't support locales

0 commit comments

Comments
 (0)