Mercurial > pub > ImplabNet
comparison Implab/Formats/JSON/JsonWriter.cs @ 228:6fa235c5a760 v2
Rewritten JsonScanner, JsonParser, fixed naming style
author | cin |
---|---|
date | Tue, 12 Sep 2017 01:19:12 +0300 |
parents | Implab/Formats/JSON/JSONWriter.cs@c32688129f14 |
children |
comparison
equal
deleted
inserted
replaced
227:8d5de4eb9c2c | 228:6fa235c5a760 |
---|---|
1 using System; | |
2 using System.Collections.Generic; | |
3 using System.IO; | |
4 using System.Globalization; | |
5 using System.Diagnostics; | |
6 | |
7 namespace Implab.Formats.Json { | |
8 public class JsonWriter { | |
9 struct Context { | |
10 public bool needComma; | |
11 public JsonElementContext element; | |
12 } | |
13 Stack<Context> m_contextStack = new Stack<Context>(); | |
14 Context m_context; | |
15 | |
16 const int BUFFER_SIZE = 64; | |
17 | |
18 TextWriter m_writer; | |
19 readonly bool m_indent = true; | |
20 readonly int m_indentSize = 4; | |
21 readonly char[] m_buffer = new char[BUFFER_SIZE]; | |
22 int m_bufferPos; | |
23 | |
24 static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; | |
25 static readonly char [] _escapeBKS, | |
26 _escapeFWD, | |
27 _escapeCR, | |
28 _escapeNL, | |
29 _escapeTAB, | |
30 _escapeBSLASH, | |
31 _escapeQ; | |
32 | |
33 static JsonWriter() { | |
34 _escapeBKS = "\\b".ToCharArray(); | |
35 _escapeFWD = "\\f".ToCharArray(); | |
36 _escapeCR = "\\r".ToCharArray(); | |
37 _escapeNL = "\\n".ToCharArray(); | |
38 _escapeTAB = "\\t".ToCharArray(); | |
39 _escapeBSLASH = "\\\\".ToCharArray(); | |
40 _escapeQ = "\\\"".ToCharArray(); | |
41 } | |
42 | |
43 public JsonWriter(TextWriter writer) { | |
44 Safe.ArgumentNotNull(writer, "writer"); | |
45 m_writer = writer; | |
46 } | |
47 | |
48 public JsonWriter(TextWriter writer, bool indent) { | |
49 Safe.ArgumentNotNull(writer, "writer"); | |
50 | |
51 m_writer = writer; | |
52 m_indent = indent; | |
53 } | |
54 | |
55 void WriteIndent() { | |
56 if (m_indent) { | |
57 var indent = new char[m_contextStack.Count * m_indentSize + 1]; | |
58 indent[0] = '\n'; | |
59 for (int i = 1; i < indent.Length; i++) | |
60 indent[i] = ' '; | |
61 m_writer.Write(new String(indent)); | |
62 } else { | |
63 m_writer.Write(' '); | |
64 } | |
65 } | |
66 | |
67 void WriteMemberName(string name) { | |
68 Safe.ArgumentNotEmpty(name, "name"); | |
69 if (m_context.element != JsonElementContext.Object) | |
70 OperationNotApplicable("WriteMember"); | |
71 if (m_context.needComma) | |
72 m_writer.Write(","); | |
73 | |
74 WriteIndent(); | |
75 m_context.needComma = true; | |
76 Write(name); | |
77 m_writer.Write(" : "); | |
78 } | |
79 | |
80 public void WriteValue(string name, string value) { | |
81 WriteMemberName(name); | |
82 Write(value); | |
83 } | |
84 | |
85 public void WriteValue(string name, bool value) { | |
86 WriteMemberName(name); | |
87 Write(value); | |
88 } | |
89 | |
90 public void WriteValue(string name, double value) { | |
91 WriteMemberName(name); | |
92 Write(value); | |
93 } | |
94 | |
95 public void WriteValue(string value) { | |
96 if (m_context.element == JsonElementContext.Array) { | |
97 | |
98 if (m_context.needComma) | |
99 m_writer.Write(","); | |
100 WriteIndent(); | |
101 m_context.needComma = true; | |
102 | |
103 Write(value); | |
104 } else if (m_context.element == JsonElementContext.None) { | |
105 Write(value); | |
106 m_context.element = JsonElementContext.Closed; | |
107 } else { | |
108 OperationNotApplicable("WriteValue"); | |
109 } | |
110 } | |
111 | |
112 public void WriteValue(bool value) { | |
113 if (m_context.element == JsonElementContext.Array) { | |
114 | |
115 if (m_context.needComma) | |
116 m_writer.Write(","); | |
117 WriteIndent(); | |
118 m_context.needComma = true; | |
119 | |
120 Write(value); | |
121 } else if (m_context.element == JsonElementContext.None) { | |
122 Write(value); | |
123 m_context.element = JsonElementContext.Closed; | |
124 } else { | |
125 OperationNotApplicable("WriteValue"); | |
126 } | |
127 } | |
128 | |
129 public void WriteValue(double value) { | |
130 if (m_context.element == JsonElementContext.Array) { | |
131 | |
132 if (m_context.needComma) | |
133 m_writer.Write(","); | |
134 WriteIndent(); | |
135 m_context.needComma = true; | |
136 | |
137 Write(value); | |
138 } else if (m_context.element == JsonElementContext.None) { | |
139 Write(value); | |
140 m_context.element = JsonElementContext.Closed; | |
141 } else { | |
142 OperationNotApplicable("WriteValue"); | |
143 } | |
144 } | |
145 | |
146 public void BeginObject() { | |
147 if (m_context.element != JsonElementContext.None && m_context.element != JsonElementContext.Array) | |
148 OperationNotApplicable("BeginObject"); | |
149 if (m_context.needComma) | |
150 m_writer.Write(","); | |
151 | |
152 WriteIndent(); | |
153 | |
154 m_context.needComma = true; | |
155 | |
156 m_contextStack.Push(m_context); | |
157 | |
158 m_context = new Context { element = JsonElementContext.Object, needComma = false }; | |
159 m_writer.Write("{"); | |
160 } | |
161 | |
162 public void BeginObject(string name) { | |
163 WriteMemberName(name); | |
164 | |
165 m_contextStack.Push(m_context); | |
166 | |
167 m_context = new Context { element = JsonElementContext.Object, needComma = false }; | |
168 m_writer.Write("{"); | |
169 } | |
170 | |
171 public void EndObject() { | |
172 if (m_context.element != JsonElementContext.Object) | |
173 OperationNotApplicable("EndObject"); | |
174 | |
175 m_context = m_contextStack.Pop(); | |
176 if (m_contextStack.Count == 0) | |
177 m_context.element = JsonElementContext.Closed; | |
178 WriteIndent(); | |
179 m_writer.Write("}"); | |
180 } | |
181 | |
182 public void BeginArray() { | |
183 if (m_context.element != JsonElementContext.None && m_context.element != JsonElementContext.Array) | |
184 throw new InvalidOperationException(); | |
185 if (m_context.needComma) { | |
186 m_writer.Write(","); | |
187 | |
188 } | |
189 m_context.needComma = true; | |
190 | |
191 WriteIndent(); | |
192 m_contextStack.Push(m_context); | |
193 m_context = new Context { element = JsonElementContext.Array, needComma = false }; | |
194 m_writer.Write("["); | |
195 } | |
196 | |
197 public void BeginArray(string name) { | |
198 WriteMemberName(name); | |
199 | |
200 m_contextStack.Push(m_context); | |
201 | |
202 m_context = new Context { element = JsonElementContext.Array, needComma = false }; | |
203 m_writer.Write("["); | |
204 } | |
205 | |
206 public void EndArray() { | |
207 if (m_context.element != JsonElementContext.Array) | |
208 OperationNotApplicable("EndArray"); | |
209 | |
210 m_context = m_contextStack.Pop(); | |
211 if (m_contextStack.Count == 0) | |
212 m_context.element = JsonElementContext.Closed; | |
213 WriteIndent(); | |
214 m_writer.Write("]"); | |
215 } | |
216 | |
217 void Write(bool value) { | |
218 m_writer.Write(value ? "true" : "false"); | |
219 } | |
220 | |
221 void FlushBuffer() { | |
222 if (m_bufferPos > 0) { | |
223 m_writer.Write(m_buffer, 0, m_bufferPos); | |
224 m_bufferPos = 0; | |
225 } | |
226 } | |
227 | |
228 void Write(string value) { | |
229 if (value == null) { | |
230 m_writer.Write("null"); | |
231 return; | |
232 } | |
233 | |
234 Debug.Assert(m_bufferPos == 0); | |
235 | |
236 var chars = value.ToCharArray(); | |
237 m_buffer[m_bufferPos++] = '"'; | |
238 | |
239 // Analysis disable once ForCanBeConvertedToForeach | |
240 for (int i = 0; i < chars.Length; i++) { | |
241 var ch = chars[i]; | |
242 | |
243 char[] escapeSeq; | |
244 | |
245 switch (ch) { | |
246 case '\b': | |
247 escapeSeq = _escapeBKS; | |
248 break; | |
249 case '\f': | |
250 escapeSeq = _escapeFWD; | |
251 break; | |
252 case '\r': | |
253 escapeSeq = _escapeCR; | |
254 break; | |
255 case '\n': | |
256 escapeSeq = _escapeNL; | |
257 break; | |
258 case '\t': | |
259 escapeSeq = _escapeTAB; | |
260 break; | |
261 case '\\': | |
262 escapeSeq = _escapeBSLASH; | |
263 break; | |
264 case '"': | |
265 escapeSeq = _escapeQ; | |
266 break; | |
267 default: | |
268 if (ch < 0x20) { | |
269 if (m_bufferPos + 6 > BUFFER_SIZE) | |
270 FlushBuffer(); | |
271 | |
272 m_buffer[m_bufferPos++] = '\\'; | |
273 m_buffer[m_bufferPos++] = 'u'; | |
274 m_buffer[m_bufferPos++] = '0'; | |
275 m_buffer[m_bufferPos++] = '0'; | |
276 m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf]; | |
277 m_buffer[m_bufferPos++] = _hex[ch & 0xf]; | |
278 | |
279 } else { | |
280 if (m_bufferPos >= BUFFER_SIZE) | |
281 FlushBuffer(); | |
282 m_buffer[m_bufferPos++] = ch; | |
283 } | |
284 continue; | |
285 } | |
286 | |
287 if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE) | |
288 FlushBuffer(); | |
289 | |
290 Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length); | |
291 m_bufferPos += escapeSeq.Length; | |
292 | |
293 } | |
294 | |
295 if (m_bufferPos >= BUFFER_SIZE) | |
296 FlushBuffer(); | |
297 | |
298 m_buffer[m_bufferPos++] = '"'; | |
299 | |
300 FlushBuffer(); | |
301 } | |
302 | |
303 void Write(double value) { | |
304 if (double.IsNaN(value)) | |
305 Write("NaN"); | |
306 else if (double.IsNegativeInfinity(value)) | |
307 Write("-Infinity"); | |
308 else if (double.IsPositiveInfinity(value)) | |
309 Write("Infinity"); | |
310 else | |
311 m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); | |
312 } | |
313 | |
314 void OperationNotApplicable(string opName) { | |
315 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); | |
316 } | |
317 | |
318 } | |
319 } |