JSONWriter.cs
256 lines
| 7.9 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; | ||||
cin
|
r72 | readonly bool m_indent = true; | ||
readonly int m_indentSize = 4; | ||||
cin
|
r55 | |||
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; | ||||
} | ||||
cin
|
r72 | 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(' '); | ||||
} | ||||
} | ||||
cin
|
r55 | void WriteMemberName(string name) { | ||
Safe.ArgumentNotEmpty(name, "name"); | ||||
if (m_context.element != JSONElementContext.Object) | ||||
OperationNotApplicable("WriteMember"); | ||||
if (m_context.needComma) | ||||
cin
|
r72 | m_writer.Write(","); | ||
WriteIndent(); | ||||
cin
|
r55 | 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) | ||||
cin
|
r72 | m_writer.Write(","); | ||
WriteIndent(); | ||||
cin
|
r55 | m_context.needComma = true; | ||
Write(value); | ||||
} | ||||
public void WriteValue(bool value) { | ||||
if (m_context.element != JSONElementContext.Array) | ||||
OperationNotApplicable("WriteValue"); | ||||
if (m_context.needComma) | ||||
cin
|
r72 | m_writer.Write(","); | ||
cin
|
r55 | m_context.needComma = true; | ||
cin
|
r72 | WriteIndent(); | ||
cin
|
r55 | Write(value); | ||
} | ||||
public void WriteValue(double value) { | ||||
if (m_context.element != JSONElementContext.Array) | ||||
OperationNotApplicable("WriteValue"); | ||||
if (m_context.needComma) | ||||
cin
|
r72 | m_writer.Write(","); | ||
cin
|
r55 | m_context.needComma = true; | ||
cin
|
r72 | WriteIndent(); | ||
cin
|
r55 | Write(value); | ||
} | ||||
public void BeginObject() { | ||||
if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | ||||
OperationNotApplicable("BeginObject"); | ||||
if (m_context.needComma) | ||||
cin
|
r72 | m_writer.Write(","); | ||
WriteIndent(); | ||||
cin
|
r55 | m_context.needComma = true; | ||
m_contextStack.Push(m_context); | ||||
m_context = new Context { element = JSONElementContext.Object, needComma = false }; | ||||
cin
|
r72 | m_writer.Write("{"); | ||
cin
|
r55 | } | ||
public void BeginObject(string name) { | ||||
WriteMemberName(name); | ||||
m_contextStack.Push(m_context); | ||||
m_context = new Context { element = JSONElementContext.Object, needComma = false }; | ||||
cin
|
r72 | m_writer.Write("{"); | ||
cin
|
r55 | } | ||
public void EndObject() { | ||||
if (m_context.element != JSONElementContext.Object) | ||||
OperationNotApplicable("EndArray"); | ||||
cin
|
r72 | |||
cin
|
r55 | m_context = m_contextStack.Pop(); | ||
cin
|
r72 | WriteIndent(); | ||
m_writer.Write("}"); | ||||
cin
|
r55 | } | ||
public void BeginArray() { | ||||
if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | ||||
throw new InvalidOperationException(); | ||||
cin
|
r72 | if (m_context.needComma) { | ||
m_writer.Write(","); | ||||
} | ||||
cin
|
r55 | m_context.needComma = true; | ||
cin
|
r72 | WriteIndent(); | ||
cin
|
r55 | m_contextStack.Push(m_context); | ||
m_context = new Context { element = JSONElementContext.Array, needComma = false }; | ||||
cin
|
r72 | m_writer.Write("["); | ||
cin
|
r55 | } | ||
public void BeginArray(string name) { | ||||
WriteMemberName(name); | ||||
m_contextStack.Push(m_context); | ||||
m_context = new Context { element = JSONElementContext.Array, needComma = false }; | ||||
cin
|
r72 | m_writer.Write("["); | ||
cin
|
r55 | } | ||
public void EndArray() { | ||||
if (m_context.element != JSONElementContext.Array) | ||||
OperationNotApplicable("EndArray"); | ||||
m_context = m_contextStack.Pop(); | ||||
cin
|
r72 | WriteIndent(); | ||
m_writer.Write("]"); | ||||
cin
|
r55 | } | ||
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 )); | ||||
} | ||||
} | ||||
} | ||||