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