blob: f387f39d16bfd752e3e0f3091eff763ad7ea9a6b [file] [log] [blame]
csharptest60fd7732011-09-09 12:18:16 -05001using System;
csharptest74c5e0c2011-07-14 13:06:22 -05002using System.Collections;
csharptestafe844b2011-06-10 16:03:22 -05003using System.Collections.Generic;
4using System.IO;
csharptest7fc785c2011-06-10 23:54:53 -05005using System.Text;
csharptestafe844b2011-06-10 16:03:22 -05006using Google.ProtocolBuffers.Descriptors;
7
8namespace Google.ProtocolBuffers.Serialization
9{
10 /// <summary>
11 /// JsonFormatWriter is a .NET 2.0 friendly json formatter for proto buffer messages. For .NET 3.5
12 /// you may also use the XmlFormatWriter with an XmlWriter created by the
13 /// <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory">JsonReaderWriterFactory</see>.
14 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050015 public abstract class JsonFormatWriter : AbstractTextWriter
csharptestafe844b2011-06-10 16:03:22 -050016 {
csharptest7fc785c2011-06-10 23:54:53 -050017 #region buffering implementations
csharptest74c5e0c2011-07-14 13:06:22 -050018
csharptest7fc785c2011-06-10 23:54:53 -050019 private class JsonTextWriter : JsonFormatWriter
csharptestafe844b2011-06-10 16:03:22 -050020 {
csharptest7fc785c2011-06-10 23:54:53 -050021 private readonly char[] _buffer;
22 private TextWriter _output;
csharptest74c5e0c2011-07-14 13:06:22 -050023 private int _bufferPos;
csharptestafe844b2011-06-10 16:03:22 -050024
csharptest7fc785c2011-06-10 23:54:53 -050025 public JsonTextWriter(TextWriter output)
csharptestafe844b2011-06-10 16:03:22 -050026 {
csharptest7fc785c2011-06-10 23:54:53 -050027 _buffer = new char[4096];
28 _bufferPos = 0;
29 _output = output;
30 _counter.Add(0);
31 }
32
33 /// <summary>
34 /// Returns the output of TextWriter.ToString() where TextWriter is the ctor argument.
35 /// </summary>
36 public override string ToString()
37 {
38 Flush();
39
40 if (_output != null)
csharptest74c5e0c2011-07-14 13:06:22 -050041 {
csharptest7fc785c2011-06-10 23:54:53 -050042 return _output.ToString();
csharptest74c5e0c2011-07-14 13:06:22 -050043 }
csharptest7fc785c2011-06-10 23:54:53 -050044
45 return new String(_buffer, 0, _bufferPos);
46 }
47
48 protected override void WriteToOutput(char[] chars, int offset, int len)
49 {
50 if (_bufferPos + len >= _buffer.Length)
csharptestafe844b2011-06-10 16:03:22 -050051 {
csharptest7fc785c2011-06-10 23:54:53 -050052 if (_output == null)
csharptest74c5e0c2011-07-14 13:06:22 -050053 {
54 _output = new StringWriter(new StringBuilder(_buffer.Length*2 + len));
55 }
csharptest7fc785c2011-06-10 23:54:53 -050056 Flush();
57 }
58
59 if (len < _buffer.Length)
60 {
61 if (len <= 12)
62 {
63 int stop = offset + len;
64 for (int i = offset; i < stop; i++)
csharptest74c5e0c2011-07-14 13:06:22 -050065 {
csharptest7fc785c2011-06-10 23:54:53 -050066 _buffer[_bufferPos++] = chars[i];
csharptest74c5e0c2011-07-14 13:06:22 -050067 }
csharptest7fc785c2011-06-10 23:54:53 -050068 }
69 else
70 {
71 Buffer.BlockCopy(chars, offset << 1, _buffer, _bufferPos << 1, len << 1);
72 _bufferPos += len;
73 }
74 }
75 else
csharptest74c5e0c2011-07-14 13:06:22 -050076 {
csharptest7fc785c2011-06-10 23:54:53 -050077 _output.Write(chars, offset, len);
csharptest74c5e0c2011-07-14 13:06:22 -050078 }
csharptest7fc785c2011-06-10 23:54:53 -050079 }
80
81 protected override void WriteToOutput(char ch)
82 {
83 if (_bufferPos >= _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -050084 {
csharptest7fc785c2011-06-10 23:54:53 -050085 Flush();
csharptest74c5e0c2011-07-14 13:06:22 -050086 }
csharptest7fc785c2011-06-10 23:54:53 -050087 _buffer[_bufferPos++] = ch;
88 }
89
90 public override void Flush()
91 {
92 if (_bufferPos > 0 && _output != null)
93 {
94 _output.Write(_buffer, 0, _bufferPos);
95 _bufferPos = 0;
96 }
97 base.Flush();
98 }
99 }
csharptest74c5e0c2011-07-14 13:06:22 -0500100
csharptest7fc785c2011-06-10 23:54:53 -0500101 private class JsonStreamWriter : JsonFormatWriter
102 {
103#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
csharptest60fd7732011-09-09 12:18:16 -0500104 static readonly Encoding Encoding = new UTF8Encoding(false);
csharptest7fc785c2011-06-10 23:54:53 -0500105#else
csharptest74c5e0c2011-07-14 13:06:22 -0500106 private static readonly Encoding Encoding = Encoding.ASCII;
csharptest7fc785c2011-06-10 23:54:53 -0500107#endif
108 private readonly byte[] _buffer;
109 private Stream _output;
csharptest74c5e0c2011-07-14 13:06:22 -0500110 private int _bufferPos;
csharptest7fc785c2011-06-10 23:54:53 -0500111
112 public JsonStreamWriter(Stream output)
113 {
114 _buffer = new byte[8192];
115 _bufferPos = 0;
116 _output = output;
117 _counter.Add(0);
118 }
119
120 protected override void WriteToOutput(char[] chars, int offset, int len)
121 {
122 if (_bufferPos + len >= _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -0500123 {
csharptest7fc785c2011-06-10 23:54:53 -0500124 Flush();
csharptest74c5e0c2011-07-14 13:06:22 -0500125 }
csharptest7fc785c2011-06-10 23:54:53 -0500126
127 if (len < _buffer.Length)
128 {
129 if (len <= 12)
130 {
131 int stop = offset + len;
132 for (int i = offset; i < stop; i++)
csharptest74c5e0c2011-07-14 13:06:22 -0500133 {
134 _buffer[_bufferPos++] = (byte) chars[i];
135 }
csharptest7fc785c2011-06-10 23:54:53 -0500136 }
137 else
138 {
139 _bufferPos += Encoding.GetBytes(chars, offset, len, _buffer, _bufferPos);
140 }
csharptestafe844b2011-06-10 16:03:22 -0500141 }
142 else
143 {
csharptest7fc785c2011-06-10 23:54:53 -0500144 byte[] temp = Encoding.GetBytes(chars, offset, len);
145 _output.Write(temp, 0, temp.Length);
csharptestafe844b2011-06-10 16:03:22 -0500146 }
147 }
csharptestafe844b2011-06-10 16:03:22 -0500148
csharptest7fc785c2011-06-10 23:54:53 -0500149 protected override void WriteToOutput(char ch)
csharptestafe844b2011-06-10 16:03:22 -0500150 {
csharptest7fc785c2011-06-10 23:54:53 -0500151 if (_bufferPos >= _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -0500152 {
csharptest7fc785c2011-06-10 23:54:53 -0500153 Flush();
csharptest74c5e0c2011-07-14 13:06:22 -0500154 }
155 _buffer[_bufferPos++] = (byte) ch;
csharptestafe844b2011-06-10 16:03:22 -0500156 }
csharptest7fc785c2011-06-10 23:54:53 -0500157
158 public override void Flush()
159 {
160 if (_bufferPos > 0 && _output != null)
161 {
162 _output.Write(_buffer, 0, _bufferPos);
163 _bufferPos = 0;
164 }
165 base.Flush();
166 }
167 }
csharptest74c5e0c2011-07-14 13:06:22 -0500168
csharptest7fc785c2011-06-10 23:54:53 -0500169 #endregion
170
171 private readonly List<int> _counter;
172 private bool _isArray;
csharptest74c5e0c2011-07-14 13:06:22 -0500173
csharptest7fc785c2011-06-10 23:54:53 -0500174 /// <summary>
175 /// Constructs a JsonFormatWriter, use the ToString() member to extract the final Json on completion.
176 /// </summary>
177 protected JsonFormatWriter()
178 {
179 _counter = new List<int>();
csharptestafe844b2011-06-10 16:03:22 -0500180 }
181
182 /// <summary>
csharptest7fc785c2011-06-10 23:54:53 -0500183 /// Constructs a JsonFormatWriter, use ToString() to extract the final output
csharptestafe844b2011-06-10 16:03:22 -0500184 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500185 public static JsonFormatWriter CreateInstance()
186 {
187 return new JsonTextWriter(null);
188 }
189
csharptest7fc785c2011-06-10 23:54:53 -0500190 /// <summary>
191 /// Constructs a JsonFormatWriter to output to the given text writer
192 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500193 public static JsonFormatWriter CreateInstance(TextWriter output)
194 {
195 return new JsonTextWriter(output);
196 }
csharptest7fc785c2011-06-10 23:54:53 -0500197
198 /// <summary>
199 /// Constructs a JsonFormatWriter to output to the given stream
200 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500201 public static JsonFormatWriter CreateInstance(Stream output)
202 {
203 return new JsonStreamWriter(output);
204 }
csharptest7fc785c2011-06-10 23:54:53 -0500205
206 /// <summary> Write to the output stream </summary>
207 protected void WriteToOutput(string format, params object[] args)
csharptest74c5e0c2011-07-14 13:06:22 -0500208 {
209 WriteToOutput(String.Format(format, args));
210 }
211
csharptest7fc785c2011-06-10 23:54:53 -0500212 /// <summary> Write to the output stream </summary>
213 protected void WriteToOutput(string text)
csharptest74c5e0c2011-07-14 13:06:22 -0500214 {
215 WriteToOutput(text.ToCharArray(), 0, text.Length);
216 }
217
csharptest7fc785c2011-06-10 23:54:53 -0500218 /// <summary> Write to the output stream </summary>
219 protected abstract void WriteToOutput(char ch);
csharptest74c5e0c2011-07-14 13:06:22 -0500220
csharptest7fc785c2011-06-10 23:54:53 -0500221 /// <summary> Write to the output stream </summary>
222 protected abstract void WriteToOutput(char[] chars, int offset, int len);
csharptestafe844b2011-06-10 16:03:22 -0500223
224 /// <summary> Sets the output formatting to use Environment.NewLine with 4-character indentions </summary>
225 public JsonFormatWriter Formatted()
226 {
227 NewLine = Environment.NewLine;
228 Indent = " ";
229 Whitespace = " ";
230 return this;
231 }
232
233 /// <summary> Gets or sets the characters to use for the new-line, default = empty </summary>
234 public string NewLine { get; set; }
csharptest74c5e0c2011-07-14 13:06:22 -0500235
csharptestafe844b2011-06-10 16:03:22 -0500236 /// <summary> Gets or sets the text to use for indenting, default = empty </summary>
237 public string Indent { get; set; }
csharptest74c5e0c2011-07-14 13:06:22 -0500238
csharptestafe844b2011-06-10 16:03:22 -0500239 /// <summary> Gets or sets the whitespace to use to separate the text, default = empty </summary>
240 public string Whitespace { get; set; }
241
csharptest819b7152011-09-08 20:28:22 -0500242 /// <summary>
243 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
244 /// </summary>
245 protected override void Dispose(bool disposing)
246 {
csharptest60fd7732011-09-09 12:18:16 -0500247 if (disposing)
csharptest819b7152011-09-08 20:28:22 -0500248 {
csharptest60fd7732011-09-09 12:18:16 -0500249 while(_counter.Count > 1)
250 WriteMessageEnd();
csharptest819b7152011-09-08 20:28:22 -0500251 }
252
253 base.Dispose(disposing);
254 }
255
csharptestafe844b2011-06-10 16:03:22 -0500256 private void Seperator()
257 {
258 if (_counter.Count == 0)
csharptest74c5e0c2011-07-14 13:06:22 -0500259 {
csharptestafe844b2011-06-10 16:03:22 -0500260 throw new InvalidOperationException("Missmatched open/close in Json writer.");
csharptest74c5e0c2011-07-14 13:06:22 -0500261 }
csharptestafe844b2011-06-10 16:03:22 -0500262
263 int index = _counter.Count - 1;
264 if (_counter[index] > 0)
csharptest74c5e0c2011-07-14 13:06:22 -0500265 {
csharptestafe844b2011-06-10 16:03:22 -0500266 WriteToOutput(',');
csharptest74c5e0c2011-07-14 13:06:22 -0500267 }
csharptestafe844b2011-06-10 16:03:22 -0500268
269 WriteLine(String.Empty);
270 _counter[index] = _counter[index] + 1;
271 }
272
273 private void WriteLine(string content)
274 {
275 if (!String.IsNullOrEmpty(NewLine))
276 {
277 WriteToOutput(NewLine);
278 for (int i = 1; i < _counter.Count; i++)
csharptest74c5e0c2011-07-14 13:06:22 -0500279 {
csharptestafe844b2011-06-10 16:03:22 -0500280 WriteToOutput(Indent);
csharptest74c5e0c2011-07-14 13:06:22 -0500281 }
csharptestafe844b2011-06-10 16:03:22 -0500282 }
csharptest74c5e0c2011-07-14 13:06:22 -0500283 else if (!String.IsNullOrEmpty(Whitespace))
284 {
csharptestafe844b2011-06-10 16:03:22 -0500285 WriteToOutput(Whitespace);
csharptest74c5e0c2011-07-14 13:06:22 -0500286 }
csharptestafe844b2011-06-10 16:03:22 -0500287
288 WriteToOutput(content);
289 }
290
291 private void WriteName(string field)
292 {
293 Seperator();
294 if (!String.IsNullOrEmpty(field))
295 {
296 WriteToOutput('"');
297 WriteToOutput(field);
298 WriteToOutput('"');
299 WriteToOutput(':');
300 if (!String.IsNullOrEmpty(Whitespace))
csharptest74c5e0c2011-07-14 13:06:22 -0500301 {
csharptestafe844b2011-06-10 16:03:22 -0500302 WriteToOutput(Whitespace);
csharptest74c5e0c2011-07-14 13:06:22 -0500303 }
csharptestafe844b2011-06-10 16:03:22 -0500304 }
305 }
306
307 private void EncodeText(string value)
308 {
309 char[] text = value.ToCharArray();
310 int len = text.Length;
311 int pos = 0;
312
313 while (pos < len)
314 {
315 int next = pos;
csharptest74c5e0c2011-07-14 13:06:22 -0500316 while (next < len && text[next] >= 32 && text[next] < 127 && text[next] != '\\' && text[next] != '/' &&
317 text[next] != '"')
318 {
csharptestafe844b2011-06-10 16:03:22 -0500319 next++;
csharptest74c5e0c2011-07-14 13:06:22 -0500320 }
csharptestafe844b2011-06-10 16:03:22 -0500321 WriteToOutput(text, pos, next - pos);
322 if (next < len)
323 {
324 switch (text[next])
325 {
csharptest74c5e0c2011-07-14 13:06:22 -0500326 case '"':
327 WriteToOutput(@"\""");
328 break;
329 case '\\':
330 WriteToOutput(@"\\");
331 break;
332 //odd at best to escape '/', most Json implementations don't, but it is defined in the rfc-4627
333 case '/':
334 WriteToOutput(@"\/");
335 break;
336 case '\b':
337 WriteToOutput(@"\b");
338 break;
339 case '\f':
340 WriteToOutput(@"\f");
341 break;
342 case '\n':
343 WriteToOutput(@"\n");
344 break;
345 case '\r':
346 WriteToOutput(@"\r");
347 break;
348 case '\t':
349 WriteToOutput(@"\t");
350 break;
351 default:
352 WriteToOutput(@"\u{0:x4}", (int) text[next]);
353 break;
csharptestafe844b2011-06-10 16:03:22 -0500354 }
355 next++;
356 }
357 pos = next;
358 }
359 }
360
361 /// <summary>
362 /// Writes a String value
363 /// </summary>
364 protected override void WriteAsText(string field, string textValue, object typedValue)
365 {
366 WriteName(field);
csharptest74c5e0c2011-07-14 13:06:22 -0500367 if (typedValue is bool || typedValue is int || typedValue is uint || typedValue is long ||
368 typedValue is ulong || typedValue is double || typedValue is float)
369 {
csharptestafe844b2011-06-10 16:03:22 -0500370 WriteToOutput(textValue);
csharptest74c5e0c2011-07-14 13:06:22 -0500371 }
csharptestafe844b2011-06-10 16:03:22 -0500372 else
373 {
374 WriteToOutput('"');
375 if (typedValue is string)
csharptest74c5e0c2011-07-14 13:06:22 -0500376 {
csharptestafe844b2011-06-10 16:03:22 -0500377 EncodeText(textValue);
csharptest74c5e0c2011-07-14 13:06:22 -0500378 }
csharptestafe844b2011-06-10 16:03:22 -0500379 else
csharptest74c5e0c2011-07-14 13:06:22 -0500380 {
csharptestafe844b2011-06-10 16:03:22 -0500381 WriteToOutput(textValue);
csharptest74c5e0c2011-07-14 13:06:22 -0500382 }
csharptestafe844b2011-06-10 16:03:22 -0500383 WriteToOutput('"');
384 }
385 }
386
387 /// <summary>
388 /// Writes a Double value
389 /// </summary>
390 protected override void Write(string field, double value)
391 {
392 if (double.IsNaN(value) || double.IsNegativeInfinity(value) || double.IsPositiveInfinity(value))
csharptest74c5e0c2011-07-14 13:06:22 -0500393 {
csharptestafe844b2011-06-10 16:03:22 -0500394 throw new InvalidOperationException("This format does not support NaN, Infinity, or -Infinity");
csharptest74c5e0c2011-07-14 13:06:22 -0500395 }
csharptestafe844b2011-06-10 16:03:22 -0500396 base.Write(field, value);
397 }
398
399 /// <summary>
400 /// Writes a Single value
401 /// </summary>
402 protected override void Write(string field, float value)
403 {
404 if (float.IsNaN(value) || float.IsNegativeInfinity(value) || float.IsPositiveInfinity(value))
csharptest74c5e0c2011-07-14 13:06:22 -0500405 {
csharptestafe844b2011-06-10 16:03:22 -0500406 throw new InvalidOperationException("This format does not support NaN, Infinity, or -Infinity");
csharptest74c5e0c2011-07-14 13:06:22 -0500407 }
csharptestafe844b2011-06-10 16:03:22 -0500408 base.Write(field, value);
409 }
410
411 // Treat enum as string
412 protected override void WriteEnum(string field, int number, string name)
413 {
414 Write(field, name);
415 }
416
417 /// <summary>
418 /// Writes an array of field values
419 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500420 protected override void WriteArray(FieldType type, string field, IEnumerable items)
csharptestafe844b2011-06-10 16:03:22 -0500421 {
csharptest74c5e0c2011-07-14 13:06:22 -0500422 IEnumerator enumerator = items.GetEnumerator();
423 try
424 {
425 if (!enumerator.MoveNext())
426 {
427 return;
428 }
429 }
430 finally
431 {
432 if (enumerator is IDisposable)
433 {
434 ((IDisposable) enumerator).Dispose();
435 }
436 }
csharptestafe844b2011-06-10 16:03:22 -0500437
438 WriteName(field);
439 WriteToOutput("[");
440 _counter.Add(0);
441
442 base.WriteArray(type, String.Empty, items);
443
444 _counter.RemoveAt(_counter.Count - 1);
445 WriteLine("]");
446 }
447
448 /// <summary>
449 /// Writes a message
450 /// </summary>
451 protected override void WriteMessageOrGroup(string field, IMessageLite message)
452 {
453 WriteName(field);
454 WriteMessage(message);
455 }
456
457 /// <summary>
458 /// Writes the message to the the formatted stream.
459 /// </summary>
460 public override void WriteMessage(IMessageLite message)
461 {
csharptest60fd7732011-09-09 12:18:16 -0500462 WriteMessageStart();
csharptestc2d2c1a2011-09-08 20:02:11 -0500463 message.WriteTo(this);
csharptest60fd7732011-09-09 12:18:16 -0500464 WriteMessageEnd();
csharptestc2d2c1a2011-09-08 20:02:11 -0500465 }
466
467 /// <summary>
468 /// Used to write the root-message preamble, in json this is the left-curly brace '{'.
469 /// After this call you can call IMessageLite.MergeTo(...) and complete the message with
csharptest60fd7732011-09-09 12:18:16 -0500470 /// a call to WriteMessageEnd().
csharptestc2d2c1a2011-09-08 20:02:11 -0500471 /// </summary>
csharptest60fd7732011-09-09 12:18:16 -0500472 public override void WriteMessageStart()
csharptestc2d2c1a2011-09-08 20:02:11 -0500473 {
csharptest74c5e0c2011-07-14 13:06:22 -0500474 if (_isArray)
475 {
476 Seperator();
477 }
csharptestafe844b2011-06-10 16:03:22 -0500478 WriteToOutput("{");
479 _counter.Add(0);
csharptestc2d2c1a2011-09-08 20:02:11 -0500480 }
481
482 /// <summary>
csharptest60fd7732011-09-09 12:18:16 -0500483 /// Used to complete a root-message previously started with a call to WriteMessageStart()
csharptestc2d2c1a2011-09-08 20:02:11 -0500484 /// </summary>
csharptest60fd7732011-09-09 12:18:16 -0500485 public override void WriteMessageEnd()
csharptestc2d2c1a2011-09-08 20:02:11 -0500486 {
csharptestafe844b2011-06-10 16:03:22 -0500487 _counter.RemoveAt(_counter.Count - 1);
488 WriteLine("}");
489 Flush();
490 }
491
492 /// <summary>
csharptestafe844b2011-06-10 16:03:22 -0500493 /// Used in streaming arrays of objects to the writer
494 /// </summary>
495 /// <example>
496 /// <code>
497 /// using(writer.StartArray())
498 /// foreach(IMessageLite m in messages)
499 /// writer.WriteMessage(m);
500 /// </code>
501 /// </example>
csharptest74c5e0c2011-07-14 13:06:22 -0500502 public sealed class JsonArray : IDisposable
csharptestafe844b2011-06-10 16:03:22 -0500503 {
csharptest74c5e0c2011-07-14 13:06:22 -0500504 private JsonFormatWriter _writer;
505
csharptestafe844b2011-06-10 16:03:22 -0500506 internal JsonArray(JsonFormatWriter writer)
507 {
508 _writer = writer;
509 _writer.WriteToOutput("[");
510 _writer._counter.Add(0);
511 }
512
513 /// <summary>
514 /// Causes the end of the array character to be written.
515 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500516 private void EndArray()
csharptestafe844b2011-06-10 16:03:22 -0500517 {
518 if (_writer != null)
519 {
520 _writer._counter.RemoveAt(_writer._counter.Count - 1);
521 _writer.WriteLine("]");
522 _writer.Flush();
523 }
csharptest74c5e0c2011-07-14 13:06:22 -0500524 _writer = null;
csharptestafe844b2011-06-10 16:03:22 -0500525 }
csharptest74c5e0c2011-07-14 13:06:22 -0500526
527 void IDisposable.Dispose()
528 {
529 EndArray();
530 }
csharptestafe844b2011-06-10 16:03:22 -0500531 }
532
533 /// <summary>
534 /// Used to write an array of messages as the output rather than a single message.
535 /// </summary>
536 /// <example>
537 /// <code>
538 /// using(writer.StartArray())
539 /// foreach(IMessageLite m in messages)
540 /// writer.WriteMessage(m);
541 /// </code>
542 /// </example>
543 public JsonArray StartArray()
544 {
csharptest74c5e0c2011-07-14 13:06:22 -0500545 if (_isArray)
546 {
547 Seperator();
548 }
csharptestafe844b2011-06-10 16:03:22 -0500549 _isArray = true;
550 return new JsonArray(this);
551 }
552 }
553}