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