view Implab/JSON/JSONWriter.cs @ 65:653c4e04968b

minor changes
author cin
date Mon, 30 Jun 2014 13:55:22 +0400
parents c0bf853aa04f
children d67b95eddaf4
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Implab.JSON {
    public class JSONWriter {
        struct Context {
            public bool needComma;
            public JSONElementContext element;
        }
        Stack<Context> m_contextStack = new Stack<Context>();
        Context m_context;

        TextWriter m_writer;
        bool m_indent;

        static readonly char [] _escapeBKS,
            _escapeFWD,
            _escapeCR,
            _escapeNL,
            _escapeTAB,
            _escapeSLASH,
            _escapeBSLASH,
            _escapeQ;

        static JSONWriter() {
            _escapeBKS = "\\b".ToCharArray();
            _escapeFWD = "\\f".ToCharArray();
            _escapeCR = "\\r".ToCharArray();
            _escapeNL = "\\n".ToCharArray();
            _escapeTAB = "\\t".ToCharArray();
            _escapeBSLASH = "\\\\".ToCharArray();
            _escapeSLASH = "\\/".ToCharArray();
            _escapeQ = "\\\"".ToCharArray();
        }

        public JSONWriter(TextWriter writer) {
            Safe.ArgumentNotNull(writer, "writer");

            m_writer = writer;
        }

        void WriteMemberName(string name) {
            Safe.ArgumentNotEmpty(name, "name");
            if (m_context.element != JSONElementContext.Object)
                OperationNotApplicable("WriteMember");
            if (m_context.needComma)
                m_writer.Write(", ");
            // TODO indent
            m_context.needComma = true;
            Write(name);
            m_writer.Write(" : ");
        }

        public void WriteValue(string name, string value) {
            WriteMemberName(name);
            Write(value);            
        }

        public void WriteValue(string name, bool value) {
            WriteMemberName(name);
            Write(value);
        }

        public void WriteValue(string name, double value) {
            WriteMemberName(name);
            Write(value);
        }



        public void WriteValue(string value) {
            if (m_context.element != JSONElementContext.Array)
                OperationNotApplicable("WriteValue");
            if (m_context.needComma)
                m_writer.Write(", ");
            m_context.needComma = true;

            Write(value);
        }

        public void WriteValue(bool value) {
            if (m_context.element != JSONElementContext.Array)
                OperationNotApplicable("WriteValue");
            if (m_context.needComma)
                m_writer.Write(", ");
            m_context.needComma = true;

            Write(value);
        }

        public void WriteValue(double value) {
            if (m_context.element != JSONElementContext.Array)
                OperationNotApplicable("WriteValue");
            if (m_context.needComma)
                m_writer.Write(", ");
            m_context.needComma = true;

            Write(value);
        }
        
        public void BeginObject() {
            if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
                OperationNotApplicable("BeginObject");
            if (m_context.needComma)
                m_writer.Write(", ");
            m_context.needComma = true;

            m_contextStack.Push(m_context);

            m_context = new Context { element = JSONElementContext.Object, needComma = false };
            m_writer.Write("{ ");
        }

        public void BeginObject(string name) {
            WriteMemberName(name);

            m_contextStack.Push(m_context);

            m_context = new Context { element = JSONElementContext.Object, needComma = false };
            m_writer.Write("{ ");
        }

        public void EndObject() {
            if (m_context.element != JSONElementContext.Object)
                OperationNotApplicable("EndArray");
            
            m_writer.Write(" }");
            m_context = m_contextStack.Pop();
        }

        public void BeginArray() {
            if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
                throw new InvalidOperationException();
            if (m_context.needComma)
                m_writer.Write(", ");
            m_context.needComma = true;

            m_contextStack.Push(m_context);

            m_context = new Context { element = JSONElementContext.Array, needComma = false };
            m_writer.Write("[ ");
        }

        public void BeginArray(string name) {
            WriteMemberName(name);

            m_contextStack.Push(m_context);

            m_context = new Context { element = JSONElementContext.Array, needComma = false };
            m_writer.Write("[ ");
        }

        public void EndArray() {
            if (m_context.element != JSONElementContext.Array)
                OperationNotApplicable("EndArray");

            m_writer.Write(" ]");
            m_context = m_contextStack.Pop();
        }

        void Write(bool value) {
            m_writer.Write(value ? "true" : "false");
        }
 

        void Write(string value) {
            if (value == null)
                m_writer.Write("null");

            var chars = value.ToCharArray();
            m_writer.Write('"');
            
            for (int i = 0; i < chars.Length; i++) {
                var ch = chars[i];

                switch (ch) {
                    case '\b':
                        m_writer.Write(_escapeBKS);
                        break;
                    case '\f':
                        m_writer.Write(_escapeFWD);
                        break;
                    case '\r':
                        m_writer.Write(_escapeCR);
                        break;
                    case '\n':
                        m_writer.Write(_escapeNL);
                        break;
                    case '\t':
                        m_writer.Write(_escapeTAB);
                        break;
                    case '\\':
                        m_writer.Write(_escapeBSLASH);
                        break;
                    case '/':
                        m_writer.Write(_escapeSLASH);
                        break;
                    case '"':
                        m_writer.Write(_escapeQ);
                        break;
                    default:
                        if (ch < 0x20) {
                            m_writer.Write("\\u00{0:x2}",(int)ch);
                        } else {
                            m_writer.Write(ch);
                        }
                        break;
                }
            }

            m_writer.Write('"');
        }

        void Write(double value) {
            m_writer.Write(value);
        }

        void OperationNotApplicable(string opName) {
            throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element ));
        }
        
    }
}