summaryrefslogtreecommitdiffhomepage
path: root/tests/custom/03_stdlib/34_json
blob: c30d6d092b5b03394e34a17f3d0dda95cb89c3b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
The `json()` function parses the given string value as JSON.

Throws an exception if the given input value is not a string.
Throws an exception if the given input string cannot be parsed as JSON.

Returns the resulting value.

-- Testcase --
{%
	print(join("\n", [
		json("null"),
		json("true"),
		json("false"),
		json("123"),
		json("456.7890000"),
		json("-1.4E10"),
		json("1e309"),
		json('"A string \u2600"'),
		json("[ 1, 2, 3 ]"),
		json('{ "test": [ 1, 2, 3 ] }'),

		// surrounding white space is ignored
		json('    [ 1, 2, 3 ]    ')
	]), "\n");
%}
-- End --

-- Expect stdout --
null
true
false
123
456.789
-1.4e+10
Infinity
A string ☀
[ 1, 2, 3 ]
{ "test": [ 1, 2, 3 ] }
[ 1, 2, 3 ]
-- End --


Passing a non-string value throws an exception.

-- Testcase --
{%
	json(true);
%}
-- End --

-- Expect stderr --
Type error: Passed value is neither a string nor an object
In line 2, byte 11:

 `    json(true);`
  Near here ---^


-- End --


Unparseable JSON throws exceptions.

-- Testcase --
{%
	json('[ "incomplete", "array" ');
%}
-- End --

-- Expect stderr --
Syntax error: Failed to parse JSON string: unexpected end of data
In line 2, byte 33:

 `    json('[ "incomplete", "array" ');`
  Near here -------------------------^


-- End --

-- Testcase --
{%
	json('invalid syntax');
%}
-- End --

-- Expect stderr --
Syntax error: Failed to parse JSON string: unexpected character
In line 2, byte 23:

 `    json('invalid syntax');`
  Near here ---------------^


-- End --

-- Testcase --
{%
	json('[] trailing garbage');
%}
-- End --

-- Expect stderr --
Syntax error: Trailing garbage after JSON data
In line 2, byte 28:

 `    json('[] trailing garbage');`
  Near here --------------------^


-- End --


Additionally, `json()` accepts objects implementing a read method as input.
During JSON parsing, the read method is repeatedly invoked with a buffer size
hint as sole argument. The return value of the read method is converted to a
string if needed and passed on to the JSON parser. A `null` or an empty string
return value is treated as EOF, ending the parse process.

-- Testcase --
{%
	let fs = require("fs");

	// parse JSON from open file handle
	printf("%.J\n",
		json(fs.open("files/test.json"))
	);
%}
-- End --

-- Expect stdout --
{
	"hello": "world"
}
-- End --

-- File test.json --
{"hello":"world"}
-- End --


The `json()` function is able to parse JSON from any object providing a `read()`
method that incrementally yields JSON source data.

-- Testcase --
{%
	let parts = [
		'{"some"',
		':',
		'"object"',
		', ',
		'"etc."',
		':',
		!0,  // this is stringified to "true"
		'}'
	];

	let producer = {
		read: function(size) {
			return shift(parts);
		}
	};

	// parse JSON from producer object
	printf("%.J\n",
		json(producer)
	);
%}
-- End --

-- Expect stdout --
{
	"some": "object",
	"etc.": true
}
-- End --


Passing objects or resources not providing a `read()` method yields an exception.

-- Testcase --
{%
	json({});
%}
-- End --

-- Expect stderr --
Type error: Input object does not implement read() method
In line 2, byte 9:

 `    json({});`
  Near here -^


-- End --


Exceptions triggered by the `read()` method are properly forwarded.

-- Testcase --
{%
	json({
		read: function() {
			die("Exception in read()");
		}
	});
%}
-- End --

-- Expect stderr --
Exception in read()
In [anonymous function](), line 4, byte 29:
  called from function json ([C])
  called from anonymous function ([stdin]:6:3)

 `            die("Exception in read()");`
  Near here ---------------------------^


-- End --


EOF stops parsing and does not lead to further `read()` invocations.

-- Testcase --
{%
	let parts = [
		'["some",',
		'"JSON array",',
		'true,false,1,2,3',
		']',
		'',                  // empty string treated as EOF
		'{"some":',          // this is not reached in the first pass
		'"object"}',
		null,		         // null treated as EOF
		'"test ',            // this is not reached in the second pass
		'value"'
	];

	let producer = { read: () => shift(parts) };

	printf("%.J\n", [
		json(producer),
		json(producer),
		json(producer)
	]);
%}
-- End --

-- Expect stdout --
[
	[
		"some",
		"JSON array",
		true,
		false,
		1,
		2,
		3
	],
	{
		"some": "object"
	},
	"test value"
]
-- End --