root / Assets / Plugins / LitJson / JsonWriter.cs @ 11:01dde4258840
History | View | Annotate | Download (11.4 kB)
1 | #region Header |
---|---|
2 | /** |
3 | * JsonWriter.cs |
4 | * Stream-like facility to output JSON text. |
5 | * |
6 | * The authors disclaim copyright to this source code. For more details, see |
7 | * the COPYING file included with this distribution. |
8 | **/ |
9 | #endregion |
10 | |
11 | |
12 | using System; |
13 | using System.Collections.Generic; |
14 | using System.Globalization; |
15 | using System.IO; |
16 | using System.Text; |
17 | |
18 | |
19 | namespace LitJson |
20 | { |
21 | internal enum Condition |
22 | { |
23 | InArray, |
24 | InObject, |
25 | NotAProperty, |
26 | Property, |
27 | Value |
28 | } |
29 | |
30 | internal class WriterContext |
31 | { |
32 | public int Count; |
33 | public bool InArray; |
34 | public bool InObject; |
35 | public bool ExpectingValue; |
36 | public int Padding; |
37 | } |
38 | |
39 | public class JsonWriter |
40 | { |
41 | #region Fields |
42 | private static NumberFormatInfo number_format; |
43 | |
44 | private WriterContext context; |
45 | private Stack<WriterContext> ctx_stack; |
46 | private bool has_reached_end; |
47 | private char[] hex_seq; |
48 | private int indentation; |
49 | private int indent_value; |
50 | private StringBuilder inst_string_builder; |
51 | private bool pretty_print; |
52 | private bool validate; |
53 | private TextWriter writer; |
54 | #endregion |
55 | |
56 | |
57 | #region Properties |
58 | public int IndentValue { |
59 | get { return indent_value; } |
60 | set { |
61 | indentation = (indentation / indent_value) * value; |
62 | indent_value = value; |
63 | } |
64 | } |
65 | |
66 | public bool PrettyPrint { |
67 | get { return pretty_print; } |
68 | set { pretty_print = value; } |
69 | } |
70 | |
71 | public TextWriter TextWriter { |
72 | get { return writer; } |
73 | } |
74 | |
75 | public bool Validate { |
76 | get { return validate; } |
77 | set { validate = value; } |
78 | } |
79 | #endregion |
80 | |
81 | |
82 | #region Constructors |
83 | static JsonWriter () |
84 | { |
85 | number_format = NumberFormatInfo.InvariantInfo; |
86 | } |
87 | |
88 | public JsonWriter () |
89 | { |
90 | inst_string_builder = new StringBuilder (); |
91 | writer = new StringWriter (inst_string_builder); |
92 | |
93 | Init (); |
94 | } |
95 | |
96 | public JsonWriter (StringBuilder sb) : |
97 | this (new StringWriter (sb)) |
98 | { |
99 | } |
100 | |
101 | public JsonWriter (TextWriter writer) |
102 | { |
103 | if (writer == null) |
104 | throw new ArgumentNullException ("writer"); |
105 | |
106 | this.writer = writer; |
107 | |
108 | Init (); |
109 | } |
110 | #endregion |
111 | |
112 | |
113 | #region Private Methods |
114 | private void DoValidation (Condition cond) |
115 | { |
116 | if (! context.ExpectingValue) |
117 | context.Count++; |
118 | |
119 | if (! validate) |
120 | return; |
121 | |
122 | if (has_reached_end) |
123 | throw new JsonException ( |
124 | "A complete JSON symbol has already been written"); |
125 | |
126 | switch (cond) { |
127 | case Condition.InArray: |
128 | if (! context.InArray) |
129 | throw new JsonException ( |
130 | "Can't close an array here"); |
131 | break; |
132 | |
133 | case Condition.InObject: |
134 | if (! context.InObject || context.ExpectingValue) |
135 | throw new JsonException ( |
136 | "Can't close an object here"); |
137 | break; |
138 | |
139 | case Condition.NotAProperty: |
140 | if (context.InObject && ! context.ExpectingValue) |
141 | throw new JsonException ( |
142 | "Expected a property"); |
143 | break; |
144 | |
145 | case Condition.Property: |
146 | if (! context.InObject || context.ExpectingValue) |
147 | throw new JsonException ( |
148 | "Can't add a property here"); |
149 | break; |
150 | |
151 | case Condition.Value: |
152 | if (! context.InArray && |
153 | (! context.InObject || ! context.ExpectingValue)) |
154 | throw new JsonException ( |
155 | "Can't add a value here"); |
156 | |
157 | break; |
158 | } |
159 | } |
160 | |
161 | private void Init () |
162 | { |
163 | has_reached_end = false; |
164 | hex_seq = new char[4]; |
165 | indentation = 0; |
166 | indent_value = 4; |
167 | pretty_print = false; |
168 | validate = true; |
169 | |
170 | ctx_stack = new Stack<WriterContext> (); |
171 | context = new WriterContext (); |
172 | ctx_stack.Push (context); |
173 | } |
174 | |
175 | private static void IntToHex (int n, char[] hex) |
176 | { |
177 | int num; |
178 | |
179 | for (int i = 0; i < 4; i++) { |
180 | num = n % 16; |
181 | |
182 | if (num < 10) |
183 | hex[3 - i] = (char) ('0' + num); |
184 | else |
185 | hex[3 - i] = (char) ('A' + (num - 10)); |
186 | |
187 | n >>= 4; |
188 | } |
189 | } |
190 | |
191 | private void Indent () |
192 | { |
193 | if (pretty_print) |
194 | indentation += indent_value; |
195 | } |
196 | |
197 | |
198 | private void Put (string str) |
199 | { |
200 | if (pretty_print && ! context.ExpectingValue) |
201 | for (int i = 0; i < indentation; i++) |
202 | writer.Write (' '); |
203 | |
204 | writer.Write (str); |
205 | } |
206 | |
207 | private void PutNewline () |
208 | { |
209 | PutNewline (true); |
210 | } |
211 | |
212 | private void PutNewline (bool add_comma) |
213 | { |
214 | if (add_comma && ! context.ExpectingValue && |
215 | context.Count > 1) |
216 | writer.Write (','); |
217 | |
218 | if (pretty_print && ! context.ExpectingValue) |
219 | writer.Write ('\n'); |
220 | } |
221 | |
222 | private void PutString (string str) |
223 | { |
224 | Put (String.Empty); |
225 | |
226 | writer.Write ('"'); |
227 | |
228 | int n = str.Length; |
229 | for (int i = 0; i < n; i++) { |
230 | switch (str[i]) { |
231 | case '\n': |
232 | writer.Write ("\\n"); |
233 | continue; |
234 | |
235 | case '\r': |
236 | writer.Write ("\\r"); |
237 | continue; |
238 | |
239 | case '\t': |
240 | writer.Write ("\\t"); |
241 | continue; |
242 | |
243 | case '"': |
244 | case '\\': |
245 | writer.Write ('\\'); |
246 | writer.Write (str[i]); |
247 | continue; |
248 | |
249 | case '\f': |
250 | writer.Write ("\\f"); |
251 | continue; |
252 | |
253 | case '\b': |
254 | writer.Write ("\\b"); |
255 | continue; |
256 | } |
257 | |
258 | if ((int) str[i] >= 32 && (int) str[i] <= 126) { |
259 | writer.Write (str[i]); |
260 | continue; |
261 | } |
262 | |
263 | // Default, turn into a \uXXXX sequence |
264 | IntToHex ((int) str[i], hex_seq); |
265 | writer.Write ("\\u"); |
266 | writer.Write (hex_seq); |
267 | } |
268 | |
269 | writer.Write ('"'); |
270 | } |
271 | |
272 | private void Unindent () |
273 | { |
274 | if (pretty_print) |
275 | indentation -= indent_value; |
276 | } |
277 | #endregion |
278 | |
279 | |
280 | public override string ToString () |
281 | { |
282 | if (inst_string_builder == null) |
283 | return String.Empty; |
284 | |
285 | return inst_string_builder.ToString (); |
286 | } |
287 | |
288 | public void Reset () |
289 | { |
290 | has_reached_end = false; |
291 | |
292 | ctx_stack.Clear (); |
293 | context = new WriterContext (); |
294 | ctx_stack.Push (context); |
295 | |
296 | if (inst_string_builder != null) |
297 | inst_string_builder.Remove (0, inst_string_builder.Length); |
298 | } |
299 | |
300 | public void Write (bool boolean) |
301 | { |
302 | DoValidation (Condition.Value); |
303 | PutNewline (); |
304 | |
305 | Put (boolean ? "true" : "false"); |
306 | |
307 | context.ExpectingValue = false; |
308 | } |
309 | |
310 | public void Write (decimal number) |
311 | { |
312 | DoValidation (Condition.Value); |
313 | PutNewline (); |
314 | |
315 | Put (Convert.ToString (number, number_format)); |
316 | |
317 | context.ExpectingValue = false; |
318 | } |
319 | |
320 | public void Write (double number) |
321 | { |
322 | DoValidation (Condition.Value); |
323 | PutNewline (); |
324 | |
325 | string str = Convert.ToString (number, number_format); |
326 | Put (str); |
327 | |
328 | if (str.IndexOf ('.') == -1 && |
329 | str.IndexOf ('E') == -1) |
330 | writer.Write (".0"); |
331 | |
332 | context.ExpectingValue = false; |
333 | } |
334 | |
335 | public void Write (int number) |
336 | { |
337 | DoValidation (Condition.Value); |
338 | PutNewline (); |
339 | |
340 | Put (Convert.ToString (number, number_format)); |
341 | |
342 | context.ExpectingValue = false; |
343 | } |
344 | |
345 | public void Write (long number) |
346 | { |
347 | DoValidation (Condition.Value); |
348 | PutNewline (); |
349 | |
350 | Put (Convert.ToString (number, number_format)); |
351 | |
352 | context.ExpectingValue = false; |
353 | } |
354 | |
355 | public void Write (string str) |
356 | { |
357 | DoValidation (Condition.Value); |
358 | PutNewline (); |
359 | |
360 | if (str == null) |
361 | Put ("null"); |
362 | else |
363 | PutString (str); |
364 | |
365 | context.ExpectingValue = false; |
366 | } |
367 | |
368 | [CLSCompliant(false)] |
369 | public void Write (ulong number) |
370 | { |
371 | DoValidation (Condition.Value); |
372 | PutNewline (); |
373 | |
374 | Put (Convert.ToString (number, number_format)); |
375 | |
376 | context.ExpectingValue = false; |
377 | } |
378 | |
379 | public void WriteArrayEnd () |
380 | { |
381 | DoValidation (Condition.InArray); |
382 | PutNewline (false); |
383 | |
384 | ctx_stack.Pop (); |
385 | if (ctx_stack.Count == 1) |
386 | has_reached_end = true; |
387 | else { |
388 | context = ctx_stack.Peek (); |
389 | context.ExpectingValue = false; |
390 | } |
391 | |
392 | Unindent (); |
393 | Put ("]"); |
394 | } |
395 | |
396 | public void WriteArrayStart () |
397 | { |
398 | DoValidation (Condition.NotAProperty); |
399 | PutNewline (); |
400 | |
401 | Put ("["); |
402 | |
403 | context = new WriterContext (); |
404 | context.InArray = true; |
405 | ctx_stack.Push (context); |
406 | |
407 | Indent (); |
408 | } |
409 | |
410 | public void WriteObjectEnd () |
411 | { |
412 | DoValidation (Condition.InObject); |
413 | PutNewline (false); |
414 | |
415 | ctx_stack.Pop (); |
416 | if (ctx_stack.Count == 1) |
417 | has_reached_end = true; |
418 | else { |
419 | context = ctx_stack.Peek (); |
420 | context.ExpectingValue = false; |
421 | } |
422 | |
423 | Unindent (); |
424 | Put ("}"); |
425 | } |
426 | |
427 | public void WriteObjectStart () |
428 | { |
429 | DoValidation (Condition.NotAProperty); |
430 | PutNewline (); |
431 | |
432 | Put ("{"); |
433 | |
434 | context = new WriterContext (); |
435 | context.InObject = true; |
436 | ctx_stack.Push (context); |
437 | |
438 | Indent (); |
439 | } |
440 | |
441 | public void WritePropertyName (string property_name) |
442 | { |
443 | DoValidation (Condition.Property); |
444 | PutNewline (); |
445 | |
446 | PutString (property_name); |
447 | |
448 | if (pretty_print) { |
449 | if (property_name.Length > context.Padding) |
450 | context.Padding = property_name.Length; |
451 | |
452 | for (int i = context.Padding - property_name.Length; |
453 | i >= 0; i--) |
454 | writer.Write (' '); |
455 | |
456 | writer.Write (": "); |
457 | } else |
458 | writer.Write (':'); |
459 | |
460 | context.ExpectingValue = true; |
461 | } |
462 | } |
463 | } |