485 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			485 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
| #region Header
 | |
| /**
 | |
|  * JsonWriter.cs
 | |
|  *   Stream-like facility to output JSON text.
 | |
|  *
 | |
|  * The authors disclaim copyright to this source code. For more details, see
 | |
|  * the COPYING file included with this distribution.
 | |
|  **/
 | |
| #endregion
 | |
| 
 | |
| 
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Globalization;
 | |
| using System.IO;
 | |
| using System.Text;
 | |
| 
 | |
| 
 | |
| namespace AnyThinkAds.ThirdParty.LitJson
 | |
| {
 | |
|     internal enum Condition
 | |
|     {
 | |
|         InArray,
 | |
|         InObject,
 | |
|         NotAProperty,
 | |
|         Property,
 | |
|         Value
 | |
|     }
 | |
| 
 | |
|     internal class WriterContext
 | |
|     {
 | |
|         public int  Count;
 | |
|         public bool InArray;
 | |
|         public bool InObject;
 | |
|         public bool ExpectingValue;
 | |
|         public int  Padding;
 | |
|     }
 | |
| 
 | |
|     public class JsonWriter
 | |
|     {
 | |
|         #region Fields
 | |
|         private static readonly NumberFormatInfo number_format;
 | |
| 
 | |
|         private WriterContext        context;
 | |
|         private Stack<WriterContext> ctx_stack;
 | |
|         private bool                 has_reached_end;
 | |
|         private char[]               hex_seq;
 | |
|         private int                  indentation;
 | |
|         private int                  indent_value;
 | |
|         private StringBuilder        inst_string_builder;
 | |
|         private bool                 pretty_print;
 | |
|         private bool                 validate;
 | |
|         private bool                 lower_case_properties;
 | |
|         private TextWriter           writer;
 | |
|         #endregion
 | |
| 
 | |
| 
 | |
|         #region Properties
 | |
|         public int IndentValue {
 | |
|             get { return indent_value; }
 | |
|             set {
 | |
|                 indentation = (indentation / indent_value) * value;
 | |
|                 indent_value = value;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public bool PrettyPrint {
 | |
|             get { return pretty_print; }
 | |
|             set { pretty_print = value; }
 | |
|         }
 | |
| 
 | |
|         public TextWriter TextWriter {
 | |
|             get { return writer; }
 | |
|         }
 | |
| 
 | |
|         public bool Validate {
 | |
|             get { return validate; }
 | |
|             set { validate = value; }
 | |
|         }
 | |
| 
 | |
|         public bool LowerCaseProperties {
 | |
|             get { return lower_case_properties; }
 | |
|             set { lower_case_properties = value; }
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
| 
 | |
|         #region Constructors
 | |
|         static JsonWriter ()
 | |
|         {
 | |
|             number_format = NumberFormatInfo.InvariantInfo;
 | |
|         }
 | |
| 
 | |
|         public JsonWriter ()
 | |
|         {
 | |
|             inst_string_builder = new StringBuilder ();
 | |
|             writer = new StringWriter (inst_string_builder);
 | |
| 
 | |
|             Init ();
 | |
|         }
 | |
| 
 | |
|         public JsonWriter (StringBuilder sb) :
 | |
|             this (new StringWriter (sb))
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         public JsonWriter (TextWriter writer)
 | |
|         {
 | |
|             if (writer == null)
 | |
|                 throw new ArgumentNullException ("writer");
 | |
| 
 | |
|             this.writer = writer;
 | |
| 
 | |
|             Init ();
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
| 
 | |
|         #region Private Methods
 | |
|         private void DoValidation (Condition cond)
 | |
|         {
 | |
|             if (! context.ExpectingValue)
 | |
|                 context.Count++;
 | |
| 
 | |
|             if (! validate)
 | |
|                 return;
 | |
| 
 | |
|             if (has_reached_end)
 | |
|                 throw new JsonException (
 | |
|                     "A complete JSON symbol has already been written");
 | |
| 
 | |
|             switch (cond) {
 | |
|             case Condition.InArray:
 | |
|                 if (! context.InArray)
 | |
|                     throw new JsonException (
 | |
|                         "Can't close an array here");
 | |
|                 break;
 | |
| 
 | |
|             case Condition.InObject:
 | |
|                 if (! context.InObject || context.ExpectingValue)
 | |
|                     throw new JsonException (
 | |
|                         "Can't close an object here");
 | |
|                 break;
 | |
| 
 | |
|             case Condition.NotAProperty:
 | |
|                 if (context.InObject && ! context.ExpectingValue)
 | |
|                     throw new JsonException (
 | |
|                         "Expected a property");
 | |
|                 break;
 | |
| 
 | |
|             case Condition.Property:
 | |
|                 if (! context.InObject || context.ExpectingValue)
 | |
|                     throw new JsonException (
 | |
|                         "Can't add a property here");
 | |
|                 break;
 | |
| 
 | |
|             case Condition.Value:
 | |
|                 if (! context.InArray &&
 | |
|                     (! context.InObject || ! context.ExpectingValue))
 | |
|                     throw new JsonException (
 | |
|                         "Can't add a value here");
 | |
| 
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void Init ()
 | |
|         {
 | |
|             has_reached_end = false;
 | |
|             hex_seq = new char[4];
 | |
|             indentation = 0;
 | |
|             indent_value = 4;
 | |
|             pretty_print = false;
 | |
|             validate = true;
 | |
|             lower_case_properties = false;
 | |
| 
 | |
|             ctx_stack = new Stack<WriterContext> ();
 | |
|             context = new WriterContext ();
 | |
|             ctx_stack.Push (context);
 | |
|         }
 | |
| 
 | |
|         private static void IntToHex (int n, char[] hex)
 | |
|         {
 | |
|             int num;
 | |
| 
 | |
|             for (int i = 0; i < 4; i++) {
 | |
|                 num = n % 16;
 | |
| 
 | |
|                 if (num < 10)
 | |
|                     hex[3 - i] = (char) ('0' + num);
 | |
|                 else
 | |
|                     hex[3 - i] = (char) ('A' + (num - 10));
 | |
| 
 | |
|                 n >>= 4;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void Indent ()
 | |
|         {
 | |
|             if (pretty_print)
 | |
|                 indentation += indent_value;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         private void Put (string str)
 | |
|         {
 | |
|             if (pretty_print && ! context.ExpectingValue)
 | |
|                 for (int i = 0; i < indentation; i++)
 | |
|                     writer.Write (' ');
 | |
| 
 | |
|             writer.Write (str);
 | |
|         }
 | |
| 
 | |
|         private void PutNewline ()
 | |
|         {
 | |
|             PutNewline (true);
 | |
|         }
 | |
| 
 | |
|         private void PutNewline (bool add_comma)
 | |
|         {
 | |
|             if (add_comma && ! context.ExpectingValue &&
 | |
|                 context.Count > 1)
 | |
|                 writer.Write (',');
 | |
| 
 | |
|             if (pretty_print && ! context.ExpectingValue)
 | |
|                 writer.Write (Environment.NewLine);
 | |
|         }
 | |
| 
 | |
|         private void PutString (string str)
 | |
|         {
 | |
|             Put (String.Empty);
 | |
| 
 | |
|             writer.Write ('"');
 | |
| 
 | |
|             int n = str.Length;
 | |
|             for (int i = 0; i < n; i++) {
 | |
|                 switch (str[i]) {
 | |
|                 case '\n':
 | |
|                     writer.Write ("\\n");
 | |
|                     continue;
 | |
| 
 | |
|                 case '\r':
 | |
|                     writer.Write ("\\r");
 | |
|                     continue;
 | |
| 
 | |
|                 case '\t':
 | |
|                     writer.Write ("\\t");
 | |
|                     continue;
 | |
| 
 | |
|                 case '"':
 | |
|                 case '\\':
 | |
|                     writer.Write ('\\');
 | |
|                     writer.Write (str[i]);
 | |
|                     continue;
 | |
| 
 | |
|                 case '\f':
 | |
|                     writer.Write ("\\f");
 | |
|                     continue;
 | |
| 
 | |
|                 case '\b':
 | |
|                     writer.Write ("\\b");
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 if ((int) str[i] >= 32 && (int) str[i] <= 126) {
 | |
|                     writer.Write (str[i]);
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 // Default, turn into a \uXXXX sequence
 | |
|                 IntToHex ((int) str[i], hex_seq);
 | |
|                 writer.Write ("\\u");
 | |
|                 writer.Write (hex_seq);
 | |
|             }
 | |
| 
 | |
|             writer.Write ('"');
 | |
|         }
 | |
| 
 | |
|         private void Unindent ()
 | |
|         {
 | |
|             if (pretty_print)
 | |
|                 indentation -= indent_value;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
| 
 | |
|         public override string ToString ()
 | |
|         {
 | |
|             if (inst_string_builder == null)
 | |
|                 return String.Empty;
 | |
| 
 | |
|             return inst_string_builder.ToString ();
 | |
|         }
 | |
| 
 | |
|         public void Reset ()
 | |
|         {
 | |
|             has_reached_end = false;
 | |
| 
 | |
|             ctx_stack.Clear ();
 | |
|             context = new WriterContext ();
 | |
|             ctx_stack.Push (context);
 | |
| 
 | |
|             if (inst_string_builder != null)
 | |
|                 inst_string_builder.Remove (0, inst_string_builder.Length);
 | |
|         }
 | |
| 
 | |
|         public void Write (bool boolean)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put (boolean ? "true" : "false");
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void Write (decimal number)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put (Convert.ToString (number, number_format));
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void Write (double number)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             string str = Convert.ToString (number, number_format);
 | |
|             Put (str);
 | |
| 
 | |
|             if (str.IndexOf ('.') == -1 &&
 | |
|                 str.IndexOf ('E') == -1)
 | |
|                 writer.Write (".0");
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void Write(float number)
 | |
|         {
 | |
|             DoValidation(Condition.Value);
 | |
|             PutNewline();
 | |
| 
 | |
|             string str = Convert.ToString(number, number_format);
 | |
|             Put(str);
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void Write (int number)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put (Convert.ToString (number, number_format));
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void Write (long number)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put (Convert.ToString (number, number_format));
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void Write (string str)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             if (str == null)
 | |
|                 Put ("null");
 | |
|             else
 | |
|                 PutString (str);
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         [CLSCompliant(false)]
 | |
|         public void Write (ulong number)
 | |
|         {
 | |
|             DoValidation (Condition.Value);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put (Convert.ToString (number, number_format));
 | |
| 
 | |
|             context.ExpectingValue = false;
 | |
|         }
 | |
| 
 | |
|         public void WriteArrayEnd ()
 | |
|         {
 | |
|             DoValidation (Condition.InArray);
 | |
|             PutNewline (false);
 | |
| 
 | |
|             ctx_stack.Pop ();
 | |
|             if (ctx_stack.Count == 1)
 | |
|                 has_reached_end = true;
 | |
|             else {
 | |
|                 context = ctx_stack.Peek ();
 | |
|                 context.ExpectingValue = false;
 | |
|             }
 | |
| 
 | |
|             Unindent ();
 | |
|             Put ("]");
 | |
|         }
 | |
| 
 | |
|         public void WriteArrayStart ()
 | |
|         {
 | |
|             DoValidation (Condition.NotAProperty);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put ("[");
 | |
| 
 | |
|             context = new WriterContext ();
 | |
|             context.InArray = true;
 | |
|             ctx_stack.Push (context);
 | |
| 
 | |
|             Indent ();
 | |
|         }
 | |
| 
 | |
|         public void WriteObjectEnd ()
 | |
|         {
 | |
|             DoValidation (Condition.InObject);
 | |
|             PutNewline (false);
 | |
| 
 | |
|             ctx_stack.Pop ();
 | |
|             if (ctx_stack.Count == 1)
 | |
|                 has_reached_end = true;
 | |
|             else {
 | |
|                 context = ctx_stack.Peek ();
 | |
|                 context.ExpectingValue = false;
 | |
|             }
 | |
| 
 | |
|             Unindent ();
 | |
|             Put ("}");
 | |
|         }
 | |
| 
 | |
|         public void WriteObjectStart ()
 | |
|         {
 | |
|             DoValidation (Condition.NotAProperty);
 | |
|             PutNewline ();
 | |
| 
 | |
|             Put ("{");
 | |
| 
 | |
|             context = new WriterContext ();
 | |
|             context.InObject = true;
 | |
|             ctx_stack.Push (context);
 | |
| 
 | |
|             Indent ();
 | |
|         }
 | |
| 
 | |
|         public void WritePropertyName (string property_name)
 | |
|         {
 | |
|             DoValidation (Condition.Property);
 | |
|             PutNewline ();
 | |
|             string propertyName = (property_name == null || !lower_case_properties)
 | |
|                 ? property_name
 | |
|                 : property_name.ToLowerInvariant();
 | |
| 
 | |
|             PutString (propertyName);
 | |
| 
 | |
|             if (pretty_print) {
 | |
|                 if (propertyName.Length > context.Padding)
 | |
|                     context.Padding = propertyName.Length;
 | |
| 
 | |
|                 for (int i = context.Padding - propertyName.Length;
 | |
|                      i >= 0; i--)
 | |
|                     writer.Write (' ');
 | |
| 
 | |
|                 writer.Write (": ");
 | |
|             } else
 | |
|                 writer.Write (':');
 | |
| 
 | |
|             context.ExpectingValue = true;
 | |
|         }
 | |
|     }
 | |
| }
 |