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 Guru.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; | ||
|  |         } | ||
|  |     } | ||
|  | } |