comparison HowTo/Mapping/MapToJson.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Globalization;
3 using System.Text;
4 using System.Xml;
5
6 using NUnit.Framework;
7
8 using BLToolkit.Mapping;
9 using BLToolkit.Reflection;
10
11 namespace HowTo.Mapping
12 {
13 public class JsonMapper : MapDataDestinationBase, IMapDataDestinationList, ISupportMapping
14 {
15 private static readonly long InitialJavaScriptDateTicks = new DateTime(1970, 1, 1).Ticks;
16
17 private string[] _fieldNames;
18 private readonly StringBuilder _sb;
19 private MappingSchema _mappingSchema;
20 private bool _scalar;
21 private bool _first;
22 private bool _firstElement;
23 private int _indent;
24
25 public JsonMapper() : this(new StringBuilder(), 0)
26 {
27 }
28
29 public JsonMapper(StringBuilder sb) : this(sb, 0)
30 {
31 }
32
33 public JsonMapper(StringBuilder sb, int indent)
34 {
35 _sb = sb;
36 _indent = indent;
37 }
38
39 public override Type GetFieldType(int index)
40 {
41 // Same as typeof(object)
42 //
43 return null;
44 }
45
46 public override int GetOrdinal(string name)
47 {
48 return Array.IndexOf(_fieldNames, name);
49 }
50
51 public override void SetValue(object o, int index, object value)
52 {
53 SetValue(o, _fieldNames[index], value);
54 }
55
56 public override void SetValue(object o, string name, object value)
57 {
58 if (!_scalar)
59 {
60 // Do not Json null values until it's an array
61 //
62 if (value == null || (value is XmlNode && IsEmptyNode((XmlNode)value)))
63 return;
64
65 if (_first)
66 _first = false;
67 else
68 _sb
69 .Append(',')
70 .AppendLine()
71 ;
72
73 for (int i = 0; i < _indent; ++i)
74 _sb.Append(' ');
75
76 _sb
77 .Append('"')
78 .Append(name)
79 .Append("\":")
80 ;
81 }
82
83 if (value == null)
84 _sb.Append("null");
85 else
86 {
87 switch (Type.GetTypeCode(value.GetType()))
88 {
89 case TypeCode.Empty:
90 case TypeCode.DBNull:
91 _sb.Append("null");
92 break;
93 case TypeCode.Boolean:
94 _sb.Append((bool)value? "true": "false");
95 break;
96 case TypeCode.Char:
97 _sb
98 .Append('\'')
99 .Append((char)value)
100 .Append('\'')
101 ;
102 break;
103 case TypeCode.SByte:
104 case TypeCode.Int16:
105 case TypeCode.Int32:
106 case TypeCode.Int64:
107 case TypeCode.Byte:
108 case TypeCode.UInt16:
109 case TypeCode.UInt32:
110 case TypeCode.UInt64:
111 case TypeCode.Single:
112 case TypeCode.Double:
113 case TypeCode.Decimal:
114 _sb.Append(((IFormattable)value).ToString(null, CultureInfo.InvariantCulture));
115 break;
116 case TypeCode.DateTime:
117 _sb
118 .Append("new Date(")
119 .Append((((DateTime)value).Ticks - InitialJavaScriptDateTicks)/10000)
120 .Append(")");
121 break;
122 case TypeCode.String:
123 _sb
124 .Append('"')
125 .Append(encode((string)value))
126 .Append('"')
127 ;
128 break;
129 default:
130 if (value is XmlNode)
131 {
132 if (IsEmptyNode((XmlNode) value))
133 _sb.Append("null");
134 else
135 WriteXmlJson((XmlNode)value);
136 }
137 else
138 {
139 JsonMapper inner = new JsonMapper(_sb, _indent + 1);
140
141 if (value.GetType().IsArray)
142 _mappingSchema.MapSourceListToDestinationList(
143 _mappingSchema.GetDataSourceList(value), inner);
144 else
145 _mappingSchema.MapSourceToDestination(
146 _mappingSchema.GetDataSource(value), value, inner, inner);
147 }
148 break;
149 }
150 }
151 }
152
153 private static string encode(string value)
154 {
155 return value.Replace("\r\n", "\\r")
156 .Replace("\n\r", "\\r")
157 .Replace("\n", "\\r")
158 .Replace("\r", "\\r")
159 .Replace("\"","\\\"");
160 }
161
162 private void WriteXmlJson(XmlNode node)
163 {
164 XmlNode textNode = GetTextNode(node);
165 if (textNode != null)
166 {
167 _sb
168 .Append("\"")
169 .Append(encode(textNode.Value))
170 .Append('\"')
171 ;
172 }
173 else
174 {
175
176 bool first = true;
177
178 _sb.Append('{');
179
180 if (node.Attributes != null)
181 {
182 foreach (XmlAttribute attr in node.Attributes)
183 {
184 if (first)
185 first = false;
186 else
187 _sb.Append(',');
188
189 _sb
190 .Append("\"@")
191 .Append(attr.Name)
192 .Append("\":\"")
193 .Append(encode(attr.Value))
194 .Append('\"')
195 ;
196 }
197 }
198
199 foreach (XmlNode child in node.ChildNodes)
200 {
201 if (IsWhitespace(child) || IsEmptyNode(child))
202 continue;
203
204 if (first)
205 first = false;
206 else
207 _sb.Append(',');
208
209 if (child is XmlText)
210 _sb
211 .Append("\"#text\":\"")
212 .Append(encode(child.Value))
213 .Append('\"')
214 ;
215 else if (child is XmlElement)
216 {
217 _sb
218 .Append('"')
219 .Append(child.Name)
220 .Append("\":")
221 ;
222 WriteXmlJson(child);
223 }
224 else
225 System.Diagnostics.Debug.Fail("Unexpected node type " + child.GetType().FullName);
226 }
227 _sb.Append('}');
228 }
229 }
230
231 private static bool IsWhitespace(XmlNode node)
232 {
233 switch (node.NodeType)
234 {
235 case XmlNodeType.Comment:
236 case XmlNodeType.Whitespace:
237 case XmlNodeType.SignificantWhitespace:
238 return true;
239 }
240 return false;
241 }
242
243 private static bool IsEmptyNode(XmlNode node)
244 {
245 if (node.Attributes != null && node.Attributes.Count > 0)
246 return false;
247
248 if (node.HasChildNodes)
249 foreach (XmlNode childNode in node.ChildNodes)
250 {
251 if (IsWhitespace(childNode) || IsEmptyNode(childNode))
252 continue;
253
254 // Not a whitespace, nor inner empty node.
255 //
256 return false;
257 }
258
259 return node.Value == null;
260 }
261
262 private static XmlNode GetTextNode(XmlNode node)
263 {
264 if (node.Attributes != null && node.Attributes.Count > 0)
265 return null;
266
267 XmlNode textNode = null;
268
269 foreach (XmlNode childNode in node.ChildNodes)
270 {
271 // Ignore all whitespace.
272 //
273 if (IsWhitespace(childNode))
274 continue;
275
276 if (childNode is XmlText)
277 {
278 // More then one text node.
279 //
280 if (textNode != null)
281 return null;
282
283 // First text node.
284 //
285 textNode = childNode;
286 }
287 else
288 // Not a text node - break;
289 //
290 return null;
291 }
292
293 return textNode;
294 }
295
296 #region ISupportMapping Members
297
298 void ISupportMapping.BeginMapping(InitContext initContext)
299 {
300 _first = true;
301 _mappingSchema = initContext.MappingSchema;
302 _fieldNames = new string[initContext.DataSource.Count];
303
304 for (int i = 0; i < _fieldNames.Length; ++i)
305 _fieldNames[i] = initContext.DataSource.GetName(i);
306
307 _scalar = _fieldNames.Length == 1 && string.IsNullOrEmpty(_fieldNames[0]);
308
309 if (_scalar)
310 return;
311
312 if (_fieldNames.Length <= 1)
313 {
314 // Reset the indent since output is a single line.
315 //
316 _indent = 0;
317 _sb.Append('{');
318 }
319 else
320 {
321 if (_indent > 0)
322 _sb.AppendLine();
323
324 for (int i = 0; i < _indent; ++i)
325 _sb.Append(' ');
326
327 _sb
328 .Append('{')
329 .AppendLine()
330 ;
331 }
332 }
333
334 void ISupportMapping.EndMapping(InitContext initContext)
335 {
336 if (_scalar)
337 return;
338
339 if (_fieldNames.Length > 1)
340 _sb.AppendLine();
341
342 for (int i = 0; i < _indent; ++i)
343 _sb.Append(' ');
344 _sb.Append('}');
345 }
346
347 #endregion
348
349 #region IMapDataDestinationList Members
350
351 void IMapDataDestinationList.InitMapping(InitContext initContext)
352 {
353 _firstElement = true;
354 _sb.Append('[');
355 }
356
357 IMapDataDestination IMapDataDestinationList.GetDataDestination(InitContext initContext)
358 {
359 return this;
360 }
361
362 object IMapDataDestinationList.GetNextObject(InitContext initContext)
363 {
364 if (_firstElement)
365 _firstElement = false;
366 else
367 _sb.Append(',');
368
369 return this;
370 }
371
372 void IMapDataDestinationList.EndMapping(InitContext initContext)
373 {
374 _sb.Append(']');
375 }
376
377 #endregion
378
379 public override string ToString()
380 {
381 return _sb.ToString();
382 }
383 }
384
385 [TestFixture]
386 public class MapToJson
387 {
388 public class Inner
389 {
390 public string Name = "inner \"object \n name";
391 }
392
393 public class Inner2
394 {
395 public string Name;
396 public int Value;
397 }
398
399 public class SourceObject
400 {
401 public string Foo = "Foo";
402 public double Bar = 1.23;
403 public DateTime Baz = DateTime.Today;
404 [MapIgnore(false)]
405 public Inner Inner = new Inner();
406 [MapIgnore(false)]
407 public Inner2 Inner2 = new Inner2();
408 public string[] StrArray = {"One", "Two", "Three"};
409 }
410
411 [Test]
412 public void Test()
413 {
414 JsonMapper jm = new JsonMapper(new StringBuilder(256));
415
416 Map./*[a]*/MapSourceToDestination/*[/a]*/(Map.GetObjectMapper(typeof(SourceObject)), new SourceObject(), jm, jm);
417 Console.Write(jm.ToString());
418
419 // Expected output:
420 //
421 // {
422 // "Foo":"Foo",
423 // "Bar":1.23,
424 // "Baz":new Date(11823840000000000),
425 // "Inner":{ "Name":"inner \"object \r name"},
426 // "Inner2":
427 // {
428 // "Name":null,
429 // "Value":0
430 // },
431 // "StrArray":["One","Two","Three"]
432 // }
433 }
434 }
435 }