diff Implab/JSON/JSONWriter.cs @ 55:c0bf853aa04f

Added initial JSON support +JSONParser +JSONWriter
author cin
date Sun, 15 Jun 2014 19:39:11 +0400
parents
children d67b95eddaf4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/JSON/JSONWriter.cs	Sun Jun 15 19:39:11 2014 +0400
@@ -0,0 +1,227 @@
+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 ));
+        }
+        
+    }
+}