Mercurial > pub > ImplabNet
annotate Implab/JSON/JSONWriter.cs @ 144:8c0b95069066 v2
DRAFT: refactoring
author | cin |
---|---|
date | Fri, 06 Mar 2015 15:45:26 +0300 |
parents | 2100965eb97f |
children | 3258399cba83 |
rev | line source |
---|---|
55 | 1 using System; |
2 using System.Collections.Generic; | |
3 using System.IO; | |
142
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
4 using System.Globalization; |
55 | 5 |
6 namespace Implab.JSON { | |
7 public class JSONWriter { | |
8 struct Context { | |
9 public bool needComma; | |
10 public JSONElementContext element; | |
11 } | |
12 Stack<Context> m_contextStack = new Stack<Context>(); | |
13 Context m_context; | |
14 | |
15 TextWriter m_writer; | |
72 | 16 readonly bool m_indent = true; |
17 readonly int m_indentSize = 4; | |
55 | 18 |
19 static readonly char [] _escapeBKS, | |
20 _escapeFWD, | |
21 _escapeCR, | |
22 _escapeNL, | |
23 _escapeTAB, | |
24 _escapeBSLASH, | |
25 _escapeQ; | |
26 | |
27 static JSONWriter() { | |
28 _escapeBKS = "\\b".ToCharArray(); | |
29 _escapeFWD = "\\f".ToCharArray(); | |
30 _escapeCR = "\\r".ToCharArray(); | |
31 _escapeNL = "\\n".ToCharArray(); | |
32 _escapeTAB = "\\t".ToCharArray(); | |
33 _escapeBSLASH = "\\\\".ToCharArray(); | |
34 _escapeQ = "\\\"".ToCharArray(); | |
35 } | |
36 | |
37 public JSONWriter(TextWriter writer) { | |
38 Safe.ArgumentNotNull(writer, "writer"); | |
39 m_writer = writer; | |
40 } | |
41 | |
72 | 42 public JSONWriter(TextWriter writer, bool indent) { |
43 Safe.ArgumentNotNull(writer, "writer"); | |
44 | |
45 m_writer = writer; | |
46 m_indent = indent; | |
47 } | |
48 | |
49 void WriteIndent() { | |
50 if (m_indent) { | |
51 var indent = new char[m_contextStack.Count * m_indentSize + 1]; | |
52 indent[0] = '\n'; | |
53 for (int i = 1; i < indent.Length; i++) | |
54 indent[i] = ' '; | |
55 m_writer.Write(new String(indent)); | |
56 } else { | |
57 m_writer.Write(' '); | |
58 } | |
59 } | |
60 | |
55 | 61 void WriteMemberName(string name) { |
62 Safe.ArgumentNotEmpty(name, "name"); | |
63 if (m_context.element != JSONElementContext.Object) | |
64 OperationNotApplicable("WriteMember"); | |
65 if (m_context.needComma) | |
72 | 66 m_writer.Write(","); |
67 | |
68 WriteIndent(); | |
55 | 69 m_context.needComma = true; |
70 Write(name); | |
71 m_writer.Write(" : "); | |
72 } | |
73 | |
74 public void WriteValue(string name, string value) { | |
75 WriteMemberName(name); | |
76 Write(value); | |
77 } | |
78 | |
79 public void WriteValue(string name, bool value) { | |
80 WriteMemberName(name); | |
81 Write(value); | |
82 } | |
83 | |
84 public void WriteValue(string name, double value) { | |
85 WriteMemberName(name); | |
86 Write(value); | |
87 } | |
88 | |
89 public void WriteValue(string value) { | |
141 | 90 if (m_context.element == JSONElementContext.Array) { |
91 | |
92 if (m_context.needComma) | |
93 m_writer.Write(","); | |
94 WriteIndent(); | |
95 m_context.needComma = true; | |
96 | |
97 Write(value); | |
98 } else if (m_context.element == JSONElementContext.None) { | |
99 Write(value); | |
100 m_context.element = JSONElementContext.Closed; | |
101 } else { | |
55 | 102 OperationNotApplicable("WriteValue"); |
141 | 103 } |
55 | 104 } |
105 | |
106 public void WriteValue(bool value) { | |
141 | 107 if (m_context.element == JSONElementContext.Array) { |
108 | |
109 if (m_context.needComma) | |
110 m_writer.Write(","); | |
111 WriteIndent(); | |
112 m_context.needComma = true; | |
113 | |
114 Write(value); | |
115 } else if (m_context.element == JSONElementContext.None) { | |
116 Write(value); | |
117 m_context.element = JSONElementContext.Closed; | |
118 } else { | |
55 | 119 OperationNotApplicable("WriteValue"); |
141 | 120 } |
55 | 121 } |
122 | |
123 public void WriteValue(double value) { | |
141 | 124 if (m_context.element == JSONElementContext.Array) { |
125 | |
126 if (m_context.needComma) | |
127 m_writer.Write(","); | |
128 WriteIndent(); | |
129 m_context.needComma = true; | |
130 | |
131 Write(value); | |
132 } else if (m_context.element == JSONElementContext.None) { | |
133 Write(value); | |
134 m_context.element = JSONElementContext.Closed; | |
135 } else { | |
55 | 136 OperationNotApplicable("WriteValue"); |
141 | 137 } |
55 | 138 } |
139 | |
140 public void BeginObject() { | |
141 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | |
142 OperationNotApplicable("BeginObject"); | |
143 if (m_context.needComma) | |
72 | 144 m_writer.Write(","); |
145 | |
146 WriteIndent(); | |
147 | |
55 | 148 m_context.needComma = true; |
149 | |
150 m_contextStack.Push(m_context); | |
151 | |
152 m_context = new Context { element = JSONElementContext.Object, needComma = false }; | |
72 | 153 m_writer.Write("{"); |
55 | 154 } |
155 | |
156 public void BeginObject(string name) { | |
157 WriteMemberName(name); | |
158 | |
159 m_contextStack.Push(m_context); | |
160 | |
161 m_context = new Context { element = JSONElementContext.Object, needComma = false }; | |
72 | 162 m_writer.Write("{"); |
55 | 163 } |
164 | |
165 public void EndObject() { | |
166 if (m_context.element != JSONElementContext.Object) | |
141 | 167 OperationNotApplicable("EndObject"); |
72 | 168 |
55 | 169 m_context = m_contextStack.Pop(); |
141 | 170 if (m_contextStack.Count == 0) |
171 m_context.element = JSONElementContext.Closed; | |
72 | 172 WriteIndent(); |
173 m_writer.Write("}"); | |
55 | 174 } |
175 | |
176 public void BeginArray() { | |
177 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | |
178 throw new InvalidOperationException(); | |
72 | 179 if (m_context.needComma) { |
180 m_writer.Write(","); | |
181 | |
182 } | |
55 | 183 m_context.needComma = true; |
184 | |
72 | 185 WriteIndent(); |
55 | 186 m_contextStack.Push(m_context); |
187 m_context = new Context { element = JSONElementContext.Array, needComma = false }; | |
72 | 188 m_writer.Write("["); |
55 | 189 } |
190 | |
191 public void BeginArray(string name) { | |
192 WriteMemberName(name); | |
193 | |
194 m_contextStack.Push(m_context); | |
195 | |
196 m_context = new Context { element = JSONElementContext.Array, needComma = false }; | |
72 | 197 m_writer.Write("["); |
55 | 198 } |
199 | |
200 public void EndArray() { | |
201 if (m_context.element != JSONElementContext.Array) | |
202 OperationNotApplicable("EndArray"); | |
203 | |
204 m_context = m_contextStack.Pop(); | |
141 | 205 if (m_contextStack.Count == 0) |
206 m_context.element = JSONElementContext.Closed; | |
72 | 207 WriteIndent(); |
208 m_writer.Write("]"); | |
55 | 209 } |
210 | |
211 void Write(bool value) { | |
212 m_writer.Write(value ? "true" : "false"); | |
213 } | |
214 | |
215 | |
216 void Write(string value) { | |
115 | 217 if (value == null) { |
55 | 218 m_writer.Write("null"); |
115 | 219 return; |
220 } | |
55 | 221 |
222 var chars = value.ToCharArray(); | |
223 m_writer.Write('"'); | |
224 | |
119
2573b562e328
Promises rewritten, added improved version of AsyncQueue
cin
parents:
118
diff
changeset
|
225 // Analysis disable once ForCanBeConvertedToForeach |
55 | 226 for (int i = 0; i < chars.Length; i++) { |
227 var ch = chars[i]; | |
228 | |
229 switch (ch) { | |
230 case '\b': | |
231 m_writer.Write(_escapeBKS); | |
232 break; | |
233 case '\f': | |
234 m_writer.Write(_escapeFWD); | |
235 break; | |
236 case '\r': | |
237 m_writer.Write(_escapeCR); | |
238 break; | |
239 case '\n': | |
240 m_writer.Write(_escapeNL); | |
241 break; | |
242 case '\t': | |
243 m_writer.Write(_escapeTAB); | |
244 break; | |
245 case '\\': | |
246 m_writer.Write(_escapeBSLASH); | |
247 break; | |
248 case '"': | |
249 m_writer.Write(_escapeQ); | |
250 break; | |
251 default: | |
252 if (ch < 0x20) { | |
253 m_writer.Write("\\u00{0:x2}",(int)ch); | |
254 } else { | |
255 m_writer.Write(ch); | |
256 } | |
257 break; | |
258 } | |
259 } | |
260 | |
261 m_writer.Write('"'); | |
262 } | |
263 | |
264 void Write(double value) { | |
142
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
265 if (double.IsNaN(value)) |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
266 Write("NaN"); |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
267 else if (double.IsNegativeInfinity(value)) |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
268 Write("-Infinity"); |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
269 else if (double.IsPositiveInfinity(value)) |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
270 Write("Infinity"); |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
271 else |
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
272 m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); |
55 | 273 } |
274 | |
275 void OperationNotApplicable(string opName) { | |
276 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); | |
277 } | |
278 | |
279 } | |
280 } |