Mercurial > pub > ImplabNet
diff Implab/Formats/Json/JsonWriter.cs @ 235:b49969a7043c v2
Слияние
author | cin |
---|---|
date | Thu, 05 Oct 2017 09:24:49 +0300 |
parents | 3e26338eb977 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Formats/Json/JsonWriter.cs Thu Oct 05 09:24:49 2017 +0300 @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Globalization; +using System.Diagnostics; + +namespace Implab.Formats.Json { + public class JsonWriter { + struct Context { + public bool needComma; + public JsonElementContext element; + } + Stack<Context> m_contextStack = new Stack<Context>(); + Context m_context; + + const int BUFFER_SIZE = 64; + + TextWriter m_writer; + readonly bool m_indent = true; + readonly int m_indentSize = 4; + readonly char[] m_buffer = new char[BUFFER_SIZE]; + int m_bufferPos; + + static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + static readonly char [] _escapeBKS, + _escapeFWD, + _escapeCR, + _escapeNL, + _escapeTAB, + _escapeBSLASH, + _escapeQ; + + static JsonWriter() { + _escapeBKS = "\\b".ToCharArray(); + _escapeFWD = "\\f".ToCharArray(); + _escapeCR = "\\r".ToCharArray(); + _escapeNL = "\\n".ToCharArray(); + _escapeTAB = "\\t".ToCharArray(); + _escapeBSLASH = "\\\\".ToCharArray(); + _escapeQ = "\\\"".ToCharArray(); + } + + public JsonWriter(TextWriter writer) { + Safe.ArgumentNotNull(writer, "writer"); + m_writer = writer; + } + + public JsonWriter(TextWriter writer, bool indent) { + Safe.ArgumentNotNull(writer, "writer"); + + m_writer = writer; + m_indent = indent; + } + + void WriteIndent() { + if (m_indent) { + var indent = new char[m_contextStack.Count * m_indentSize + 1]; + indent[0] = '\n'; + for (int i = 1; i < indent.Length; i++) + indent[i] = ' '; + m_writer.Write(new String(indent)); + } else { + m_writer.Write(' '); + } + } + + void WriteMemberName(string name) { + Safe.ArgumentNotEmpty(name, "name"); + if (m_context.element != JsonElementContext.Object) + OperationNotApplicable("WriteMember"); + if (m_context.needComma) + m_writer.Write(","); + + WriteIndent(); + 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) { + + if (m_context.needComma) + m_writer.Write(","); + WriteIndent(); + m_context.needComma = true; + + Write(value); + } else if (m_context.element == JsonElementContext.None) { + Write(value); + m_context.element = JsonElementContext.Closed; + } else { + OperationNotApplicable("WriteValue"); + } + } + + public void WriteValue(bool value) { + if (m_context.element == JsonElementContext.Array) { + + if (m_context.needComma) + m_writer.Write(","); + WriteIndent(); + m_context.needComma = true; + + Write(value); + } else if (m_context.element == JsonElementContext.None) { + Write(value); + m_context.element = JsonElementContext.Closed; + } else { + OperationNotApplicable("WriteValue"); + } + } + + public void WriteValue(double value) { + if (m_context.element == JsonElementContext.Array) { + + if (m_context.needComma) + m_writer.Write(","); + WriteIndent(); + m_context.needComma = true; + + Write(value); + } else if (m_context.element == JsonElementContext.None) { + Write(value); + m_context.element = JsonElementContext.Closed; + } else { + OperationNotApplicable("WriteValue"); + } + } + + public void BeginObject() { + if (m_context.element != JsonElementContext.None && m_context.element != JsonElementContext.Array) + OperationNotApplicable("BeginObject"); + if (m_context.needComma) + m_writer.Write(","); + + WriteIndent(); + + 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("EndObject"); + + m_context = m_contextStack.Pop(); + if (m_contextStack.Count == 0) + m_context.element = JsonElementContext.Closed; + WriteIndent(); + m_writer.Write("}"); + } + + 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; + + WriteIndent(); + 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_context = m_contextStack.Pop(); + if (m_contextStack.Count == 0) + m_context.element = JsonElementContext.Closed; + WriteIndent(); + m_writer.Write("]"); + } + + void Write(bool value) { + m_writer.Write(value ? "true" : "false"); + } + + void FlushBuffer() { + if (m_bufferPos > 0) { + m_writer.Write(m_buffer, 0, m_bufferPos); + m_bufferPos = 0; + } + } + + void Write(string value) { + if (value == null) { + m_writer.Write("null"); + return; + } + + Debug.Assert(m_bufferPos == 0); + + var chars = value.ToCharArray(); + m_buffer[m_bufferPos++] = '"'; + + // Analysis disable once ForCanBeConvertedToForeach + for (int i = 0; i < chars.Length; i++) { + var ch = chars[i]; + + char[] escapeSeq; + + switch (ch) { + case '\b': + escapeSeq = _escapeBKS; + break; + case '\f': + escapeSeq = _escapeFWD; + break; + case '\r': + escapeSeq = _escapeCR; + break; + case '\n': + escapeSeq = _escapeNL; + break; + case '\t': + escapeSeq = _escapeTAB; + break; + case '\\': + escapeSeq = _escapeBSLASH; + break; + case '"': + escapeSeq = _escapeQ; + break; + default: + if (ch < 0x20) { + if (m_bufferPos + 6 > BUFFER_SIZE) + FlushBuffer(); + + m_buffer[m_bufferPos++] = '\\'; + m_buffer[m_bufferPos++] = 'u'; + m_buffer[m_bufferPos++] = '0'; + m_buffer[m_bufferPos++] = '0'; + m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf]; + m_buffer[m_bufferPos++] = _hex[ch & 0xf]; + + } else { + if (m_bufferPos >= BUFFER_SIZE) + FlushBuffer(); + m_buffer[m_bufferPos++] = ch; + } + continue; + } + + if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE) + FlushBuffer(); + + Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length); + m_bufferPos += escapeSeq.Length; + + } + + if (m_bufferPos >= BUFFER_SIZE) + FlushBuffer(); + + m_buffer[m_bufferPos++] = '"'; + + FlushBuffer(); + } + + void Write(double value) { + if (double.IsNaN(value)) + Write("NaN"); + else if (double.IsNegativeInfinity(value)) + Write("-Infinity"); + else if (double.IsPositiveInfinity(value)) + Write("Infinity"); + else + m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); + } + + void OperationNotApplicable(string opName) { + throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); + } + + } +}