JSONWriter.cs
227 lines
| 7.1 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r55 | 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 )); | ||||
} | ||||
} | ||||
} | ||||