##// END OF EJS Templates
sync
sync

File last commit:

r163:419aa51b04fd ref20160224
r167:96681e9d0cea ref20160224
Show More
JSONWriter.cs
319 lines | 10.4 KiB | text/x-csharp | CSharpLexer
cin
JSON moved to Formats namespace...
r163 using System;
using System.Collections.Generic;
using System.IO;
using System.Globalization;
using System.Diagnostics;
namespace Implab.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 ));
}
}
}