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