source: code/Website/dot-net-library/written-by-xiao-yifang/OpenFlashChart/JSON/JsonWriter.cs

Last change on this file was 7849, checked in by dennisw, 15 years ago
File size: 27.6 KB
Line 
1#region License
2/*---------------------------------------------------------------------------------*\
3
4 Distributed under the terms of an MIT-style license:
5
6 The MIT License
7
8 Copyright (c) 2006-2009 Stephen M. McKamey
9
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files (the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
16
17 The above copyright notice and this permission notice shall be included in
18 all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 THE SOFTWARE.
27
28\*---------------------------------------------------------------------------------*/
29#endregion License
30
31using System;
32using System.IO;
33using System.ComponentModel;
34using System.Collections;
35using System.Collections.Generic;
36using System.Text;
37using System.Reflection;
38using System.Xml;
39
40namespace JsonFx.Json
41{
42 /// <summary>
43 /// Represents a proxy method for serialization of types which do not implement IJsonSerializable.
44 /// </summary>
45 /// <typeparam name="T">the type for this proxy</typeparam>
46 /// <param name="writer">the JsonWriter to serialize to</param>
47 /// <param name="value">the value to serialize</param>
48 public delegate void WriteDelegate<T>(JsonWriter writer, T value);
49
50 /// <summary>
51 /// Writer for producing JSON data.
52 /// </summary>
53 public class JsonWriter : IDisposable
54 {
55 #region Constants
56
57 public const string JsonMimeType = "application/json";
58
59 private const string AnonymousTypePrefix = "<>f__AnonymousType";
60 private const string ErrorMaxDepth = "The maxiumum depth of {0} was exceeded. Check for cycles in object graph.";
61
62 #endregion Constants
63
64 #region Fields
65
66 private readonly TextWriter writer = null;
67 private string typeHintName = null;
68 private bool strictConformance = true;
69
70 private bool prettyPrint = false;
71 private bool skipNullValue = false;
72 private bool useXmlSerializationAttributes = false;
73 private int depth = 0;
74 private int maxDepth = 25;
75 private string tab = "\t";
76 private WriteDelegate<DateTime> dateTimeSerializer = null;
77
78 #endregion Fields
79
80 #region Init
81
82 /// <summary>
83 /// Ctor.
84 /// </summary>
85 /// <param name="output">TextWriter for writing</param>
86 public JsonWriter(TextWriter output)
87 {
88 this.writer = output;
89 }
90
91 /// <summary>
92 /// Ctor.
93 /// </summary>
94 /// <param name="output">Stream for writing</param>
95 public JsonWriter(Stream output)
96 {
97 this.writer = new StreamWriter(output, Encoding.UTF8);
98 }
99
100 /// <summary>
101 /// Ctor.
102 /// </summary>
103 /// <param name="output">File name for writing</param>
104 public JsonWriter(string outputFileName)
105 {
106 Stream stream = new FileStream(outputFileName, FileMode.Create, FileAccess.Write, FileShare.Read);
107 this.writer = new StreamWriter(stream, Encoding.UTF8);
108 }
109
110 /// <summary>
111 /// Ctor.
112 /// </summary>
113 /// <param name="output">StringBuilder for appending</param>
114 public JsonWriter(StringBuilder output)
115 {
116 this.writer = new StringWriter(output, System.Globalization.CultureInfo.InvariantCulture);
117 }
118
119 #endregion Init
120
121 #region Properties
122
123 /// <summary>
124 /// Gets and sets the property name used for type hinting.
125 /// </summary>
126 public string TypeHintName
127 {
128 get { return this.typeHintName; }
129 set { this.typeHintName = value; }
130 }
131
132 /// <summary>
133 /// Gets and sets if JSON will be formatted for human reading.
134 /// </summary>
135 public bool PrettyPrint
136 {
137 get { return this.prettyPrint; }
138 set { this.prettyPrint = value; }
139 }
140
141 /// <summary>
142 /// Gets and sets the string to use for indentation
143 /// </summary>
144 public string Tab
145 {
146 get { return this.tab; }
147 set { this.tab = value; }
148 }
149
150 /// <summary>
151 /// Gets and sets the line terminator string
152 /// </summary>
153 public string NewLine
154 {
155 get { return this.writer.NewLine; }
156 set { this.writer.NewLine = value; }
157 }
158
159 /// <summary>
160 /// Gets and sets the maximum depth to be serialized.
161 /// </summary>
162 public int MaxDepth
163 {
164 get { return this.maxDepth; }
165 set
166 {
167 if (value < 1)
168 {
169 throw new ArgumentOutOfRangeException("MaxDepth must be a positive integer as it controls the maximum nesting level of serialized objects.");
170 }
171 this.maxDepth = value;
172 }
173 }
174
175 /// <summary>
176 /// Gets and sets if should use XmlSerialization Attributes.
177 /// </summary>
178 /// <remarks>
179 /// Respects XmlIgnoreAttribute, ...
180 /// </remarks>
181 public bool UseXmlSerializationAttributes
182 {
183 get { return this.useXmlSerializationAttributes; }
184 set { this.useXmlSerializationAttributes = value; }
185 }
186
187 /// <summary>
188 /// Gets and sets if should conform strictly to JSON spec.
189 /// </summary>
190 /// <remarks>
191 /// Setting to true causes NaN, Infinity, -Infinity to serialize as null.
192 /// </remarks>
193 public bool StrictConformance
194 {
195 get { return this.strictConformance; }
196 set { this.strictConformance = value; }
197 }
198
199 public bool SkipNullValue
200 {
201 get { return skipNullValue; }
202 set { skipNullValue = value; }
203 }
204 /// <summary>
205 /// Gets and sets a proxy formatter to use for DateTime serialization
206 /// </summary>
207 public WriteDelegate<DateTime> DateTimeSerializer
208 {
209 get { return this.dateTimeSerializer; }
210 set { this.dateTimeSerializer = value; }
211 }
212
213 /// <summary>
214 /// Gets the underlying TextWriter.
215 /// </summary>
216 public TextWriter TextWriter
217 {
218 get { return this.writer; }
219 }
220
221 #endregion Properties
222
223 #region Static Methods
224
225 /// <summary>
226 /// A fast method for serializing an object to JSON
227 /// </summary>
228 /// <param name="value"></param>
229 /// <returns></returns>
230 public static string Serialize(object value)
231 {
232 StringBuilder output = new StringBuilder();
233
234 using (JsonWriter writer = new JsonWriter(output))
235 {
236 writer.Write(value);
237 }
238
239 return output.ToString();
240 }
241
242 #endregion Static Methods
243
244 #region Public Methods
245
246 public void Write(object value)
247 {
248 this.Write(value, false);
249 }
250
251 protected virtual void Write(object value, bool isProperty)
252 {
253 if (isProperty && this.prettyPrint)
254 {
255 this.writer.Write(' ');
256 }
257
258 if (value == null)
259 {
260 this.writer.Write(JsonReader.LiteralNull);
261 return;
262 }
263
264 if (value is IJsonSerializable)
265 {
266 try
267 {
268 if (isProperty)
269 {
270 this.depth++;
271 if (this.depth > this.maxDepth)
272 {
273 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
274 }
275 this.WriteLine();
276 }
277 ((IJsonSerializable)value).WriteJson(this);
278 }
279 finally
280 {
281 if (isProperty)
282 {
283 this.depth--;
284 }
285 }
286 return;
287 }
288
289 // must test enumerations before value types
290 if (value is Enum)
291 {
292 this.Write((Enum)value);
293 return;
294 }
295
296 // Type.GetTypeCode() allows us to more efficiently switch type
297 // plus cannot use 'is' for ValueTypes
298 Type type = value.GetType();
299 switch (Type.GetTypeCode(type))
300 {
301 case TypeCode.Boolean:
302 {
303 this.Write((Boolean)value);
304 return;
305 }
306 case TypeCode.Byte:
307 {
308 this.Write((Byte)value);
309 return;
310 }
311 case TypeCode.Char:
312 {
313 this.Write((Char)value);
314 return;
315 }
316 case TypeCode.DateTime:
317 {
318 this.Write((DateTime)value);
319 return;
320 }
321 case TypeCode.DBNull:
322 case TypeCode.Empty:
323 {
324 this.writer.Write(JsonReader.LiteralNull);
325 return;
326 }
327 case TypeCode.Decimal:
328 {
329 // From MSDN:
330 // Conversions from Char, SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, and UInt64
331 // to Decimal are widening conversions that never lose information or throw exceptions.
332 // Conversions from Single or Double to Decimal throw an OverflowException
333 // if the result of the conversion is not representable as a Decimal.
334 this.Write((Decimal)value);
335 return;
336 }
337 case TypeCode.Double:
338 {
339 this.Write((Double)value);
340 return;
341 }
342 case TypeCode.Int16:
343 {
344 this.Write((Int16)value);
345 return;
346 }
347 case TypeCode.Int32:
348 {
349 this.Write((Int32)value);
350 return;
351 }
352 case TypeCode.Int64:
353 {
354 this.Write((Int64)value);
355 return;
356 }
357 case TypeCode.SByte:
358 {
359 this.Write((SByte)value);
360 return;
361 }
362 case TypeCode.Single:
363 {
364 this.Write((Single)value);
365 return;
366 }
367 case TypeCode.String:
368 {
369 this.Write((String)value);
370 return;
371 }
372 case TypeCode.UInt16:
373 {
374 this.Write((UInt16)value);
375 return;
376 }
377 case TypeCode.UInt32:
378 {
379 this.Write((UInt32)value);
380 return;
381 }
382 case TypeCode.UInt64:
383 {
384 this.Write((UInt64)value);
385 return;
386 }
387 default:
388 case TypeCode.Object:
389 {
390 // all others must be explicitly tested
391 break;
392 }
393 }
394
395 if (value is Guid)
396 {
397 this.Write((Guid)value);
398 return;
399 }
400
401 if (value is Uri)
402 {
403 this.Write((Uri)value);
404 return;
405 }
406
407 if (value is TimeSpan)
408 {
409 this.Write((TimeSpan)value);
410 return;
411 }
412
413 if (value is Version)
414 {
415 this.Write((Version)value);
416 return;
417 }
418
419 // IDictionary test must happen BEFORE IEnumerable test
420 // since IDictionary implements IEnumerable
421 if (value is IDictionary)
422 {
423 try
424 {
425 if (isProperty)
426 {
427 this.depth++;
428 if (this.depth > this.maxDepth)
429 {
430 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
431 }
432 this.WriteLine();
433 }
434 this.WriteObject((IDictionary)value);
435 }
436 finally
437 {
438 if (isProperty)
439 {
440 this.depth--;
441 }
442 }
443 return;
444 }
445
446 if (type.GetInterface(JsonReader.TypeGenericIDictionary) != null)
447 {
448 throw new JsonSerializationException(String.Format(JsonReader.ErrorGenericIDictionary, type));
449 }
450
451 // IDictionary test must happen BEFORE IEnumerable test
452 // since IDictionary implements IEnumerable
453 if (value is IEnumerable)
454 {
455 if (value is XmlNode)
456 {
457 this.Write((System.Xml.XmlNode)value);
458 return;
459 }
460
461 try
462 {
463 if (isProperty)
464 {
465 this.depth++;
466 if (this.depth > this.maxDepth)
467 {
468 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
469 }
470 this.WriteLine();
471 }
472 this.WriteArray((IEnumerable)value);
473 }
474 finally
475 {
476 if (isProperty)
477 {
478 this.depth--;
479 }
480 }
481 return;
482 }
483
484 // structs and classes
485 try
486 {
487 if (isProperty)
488 {
489 this.depth++;
490 if (this.depth > this.maxDepth)
491 {
492 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
493 }
494 this.WriteLine();
495 }
496 this.WriteObject(value, type);
497 }
498 finally
499 {
500 if (isProperty)
501 {
502 this.depth--;
503 }
504 }
505 }
506
507 public virtual void WriteBase64(byte[] value)
508 {
509 this.Write(Convert.ToBase64String(value));
510 }
511
512 public virtual void WriteHexString(byte[] value)
513 {
514 if (value == null || value.Length == 0)
515 {
516 this.Write(String.Empty);
517 return;
518 }
519
520 StringBuilder builder = new StringBuilder();
521
522 // Loop through each byte of the binary data
523 // and format each one as a hexadecimal string
524 for (int i=0; i<value.Length; i++)
525 {
526 builder.Append(value[i].ToString("x2"));
527 }
528
529 // the hexadecimal string
530 this.Write(builder.ToString());
531 }
532
533 public virtual void Write(DateTime value)
534 {
535 if (this.dateTimeSerializer != null)
536 {
537 this.dateTimeSerializer(this, value);
538 return;
539 }
540
541 switch (value.Kind)
542 {
543 case DateTimeKind.Local:
544 {
545 value = value.ToUniversalTime();
546 goto case DateTimeKind.Utc;
547 }
548 case DateTimeKind.Utc:
549 {
550 // UTC DateTime in ISO-8601
551 this.Write(String.Format("{0:s}Z", value));
552 break;
553 }
554 default:
555 {
556 // DateTime in ISO-8601
557 this.Write(String.Format("{0:s}", value));
558 break;
559 }
560 }
561 }
562
563 public virtual void Write(Guid value)
564 {
565 this.Write(value.ToString("D"));
566 }
567
568 public virtual void Write(Enum value)
569 {
570 string enumName = null;
571
572 Type type = value.GetType();
573
574 if (type.IsDefined(typeof(FlagsAttribute), true) && !Enum.IsDefined(type, value))
575 {
576 Enum[] flags = JsonWriter.GetFlagList(type, value);
577 string[] flagNames = new string[flags.Length];
578 for (int i=0; i<flags.Length; i++)
579 {
580 flagNames[i] = JsonNameAttribute.GetJsonName(flags[i]);
581 if (String.IsNullOrEmpty(flagNames[i]))
582 {
583 flagNames[i] = flags[i].ToString("f");
584 }
585 }
586 enumName = String.Join(", ", flagNames);
587 }
588 else
589 {
590 enumName = JsonNameAttribute.GetJsonName(value);
591 if (String.IsNullOrEmpty(enumName))
592 {
593 enumName = value.ToString("f");
594 }
595 }
596
597 this.Write(enumName);
598 }
599
600 public virtual void Write(string value)
601 {
602 if (value == null)
603 {
604 this.writer.Write(JsonReader.LiteralNull);
605 return;
606 }
607
608 int length = value.Length;
609 int start = 0;
610
611 this.writer.Write(JsonReader.OperatorStringDelim);
612
613 for (int i = start; i < length; i++)
614 {
615 if (value[i] <= '\u001F' ||
616 value[i] >= '\u007F' ||
617 value[i] == '<' ||
618 value[i] == '\t' ||
619 value[i] == JsonReader.OperatorStringDelim ||
620 value[i] == JsonReader.OperatorCharEscape)
621 {
622 this.writer.Write(value.Substring(start, i-start));
623 start = i+1;
624
625 switch (value[i])
626 {
627 case JsonReader.OperatorStringDelim:
628 case JsonReader.OperatorCharEscape:
629 {
630 this.writer.Write(JsonReader.OperatorCharEscape);
631 this.writer.Write(value[i]);
632 continue;
633 }
634 case '\b':
635 {
636 this.writer.Write("\\b");
637 continue;
638 }
639 case '\f':
640 {
641 this.writer.Write("\\f");
642 continue;
643 }
644 case '\n':
645 {
646 this.writer.Write("\\n");
647 continue;
648 }
649 case '\r':
650 {
651 this.writer.Write("\\r");
652 continue;
653 }
654 case '\t':
655 {
656 this.writer.Write("\\t");
657 continue;
658 }
659 default:
660 {
661 this.writer.Write("\\u{0:X4}", Char.ConvertToUtf32(value, i));
662 continue;
663 }
664 }
665 }
666 }
667
668 this.writer.Write(value.Substring(start, length-start));
669
670 this.writer.Write(JsonReader.OperatorStringDelim);
671 }
672
673 #endregion Public Methods
674
675 #region Primative Writer Methods
676
677 public virtual void Write(bool value)
678 {
679 this.writer.Write(value ? JsonReader.LiteralTrue : JsonReader.LiteralFalse);
680 }
681
682 public virtual void Write(byte value)
683 {
684 this.writer.Write("{0:g}", value);
685 }
686
687 public virtual void Write(sbyte value)
688 {
689 this.writer.Write("{0:g}", value);
690 }
691
692 public virtual void Write(short value)
693 {
694 this.writer.Write("{0:g}", value);
695 }
696
697 public virtual void Write(ushort value)
698 {
699 this.writer.Write("{0:g}", value);
700 }
701
702 public virtual void Write(int value)
703 {
704 this.writer.Write("{0:g}", value);
705 }
706
707 public virtual void Write(uint value)
708 {
709 this.writer.Write("{0:g}", value);
710 }
711
712 public virtual void Write(long value)
713 {
714 this.writer.Write("{0:g}", value);
715 }
716
717 public virtual void Write(ulong value)
718 {
719 this.writer.Write("{0:g}", value);
720 }
721
722 public virtual void Write(float value)
723 {
724 if (this.StrictConformance && (Single.IsNaN(value) || Single.IsInfinity(value)))
725 {
726 this.writer.Write(JsonReader.LiteralNull);
727 }
728 else
729 {
730 this.writer.Write("{0:r}", value);
731 }
732 }
733
734 public virtual void Write(double value)
735 {
736 if (this.StrictConformance && (Double.IsNaN(value) || Double.IsInfinity(value)))
737 {
738 this.writer.Write(JsonReader.LiteralNull);
739 }
740 else
741 {
742 this.writer.Write("{0:r}", value);
743 }
744 }
745
746 public virtual void Write(decimal value)
747 {
748 this.writer.Write("{0:g}", value);
749 }
750
751 public virtual void Write(char value)
752 {
753 this.Write(new String(value, 1));
754 }
755
756 public virtual void Write(TimeSpan value)
757 {
758 this.Write(value.Ticks);
759 }
760
761 public virtual void Write(Uri value)
762 {
763 this.Write(value.ToString());
764 }
765
766 public virtual void Write(Version value)
767 {
768 this.Write(value.ToString());
769 }
770
771 public virtual void Write(XmlNode value)
772 {
773 // TODO: auto-translate XML to JsonML
774 this.Write(value.OuterXml);
775 }
776
777 #endregion Primative Writer Methods
778
779 #region Writer Methods
780
781 protected internal virtual void WriteArray(IEnumerable value)
782 {
783 bool appendDelim = false;
784
785 this.writer.Write(JsonReader.OperatorArrayStart);
786
787 this.depth++;
788 if (this.depth > this.maxDepth)
789 {
790 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
791 }
792 try
793 {
794 foreach (object item in value)
795 {
796 if (appendDelim)
797 {
798 this.writer.Write(JsonReader.OperatorValueDelim);
799 }
800 else
801 {
802 appendDelim = true;
803 }
804
805 this.WriteLine();
806 this.Write(item, false);
807 }
808 }
809 finally
810 {
811 this.depth--;
812 }
813
814 if (appendDelim)
815 {
816 this.WriteLine();
817 }
818 this.writer.Write(JsonReader.OperatorArrayEnd);
819 }
820
821 protected virtual void WriteObject(IDictionary value)
822 {
823 bool appendDelim = false;
824
825 this.writer.Write(JsonReader.OperatorObjectStart);
826
827 this.depth++;
828 if (this.depth > this.maxDepth)
829 {
830 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
831 }
832 try
833 {
834 foreach (object name in value.Keys)
835 {
836 if (appendDelim)
837 {
838 this.writer.Write(JsonReader.OperatorValueDelim);
839 }
840 else
841 {
842 appendDelim = true;
843 }
844
845 this.WriteLine();
846 this.Write((String)name);
847 this.writer.Write(JsonReader.OperatorNameDelim);
848 this.Write(value[name], true);
849 }
850 }
851 finally
852 {
853 this.depth--;
854 }
855
856 if (appendDelim)
857 {
858 this.WriteLine();
859 }
860 this.writer.Write(JsonReader.OperatorObjectEnd);
861 }
862
863 protected virtual void WriteObject(object value, Type type)
864 {
865 bool appendDelim = false;
866
867 this.writer.Write(JsonReader.OperatorObjectStart);
868
869 this.depth++;
870 if (this.depth > this.maxDepth)
871 {
872 throw new JsonSerializationException(String.Format(JsonWriter.ErrorMaxDepth, this.maxDepth));
873 }
874 try
875 {
876 if (!String.IsNullOrEmpty(this.TypeHintName))
877 {
878 if (appendDelim)
879 {
880 this.writer.Write(JsonReader.OperatorValueDelim);
881 }
882 else
883 {
884 appendDelim = true;
885 }
886
887 this.WriteLine();
888 this.Write(this.TypeHintName);
889 this.writer.Write(JsonReader.OperatorNameDelim);
890 this.Write(type.FullName+", "+type.Assembly.GetName().Name, true);
891 }
892
893 bool anonymousType = type.IsGenericType && type.Name.StartsWith(JsonWriter.AnonymousTypePrefix);
894
895 // serialize public properties
896 PropertyInfo[] properties = type.GetProperties();
897 foreach (PropertyInfo property in properties)
898 {
899 if (!property.CanRead)
900 {
901 continue;
902 }
903
904 if (!property.CanWrite && !anonymousType)
905 {
906 continue;
907 }
908
909 if (this.IsIgnored(type, property, value))
910 {
911 continue;
912 }
913
914 object propertyValue = property.GetValue(value, null);
915 if ((propertyValue == null) && SkipNullValue)
916 {
917 continue;
918 }
919 if (this.IsDefaultValue(property, propertyValue))
920 {
921 continue;
922 }
923
924 if (appendDelim)
925 {
926 this.writer.Write(JsonReader.OperatorValueDelim);
927 }
928 else
929 {
930 appendDelim = true;
931 }
932
933 string propertyName = JsonNameAttribute.GetJsonName(property);
934 if (String.IsNullOrEmpty(propertyName))
935 {
936 propertyName = property.Name;
937 }
938
939 this.WriteLine();
940 this.Write(propertyName);
941 this.writer.Write(JsonReader.OperatorNameDelim);
942 this.Write(propertyValue, true);
943 }
944
945 // serialize public fields
946 FieldInfo[] fields = type.GetFields();
947 foreach (FieldInfo field in fields)
948 {
949 if (!field.IsPublic || field.IsStatic)
950 {
951 continue;
952 }
953
954 if (this.IsIgnored(type, field, value))
955 {
956 continue;
957 }
958
959 object fieldValue = field.GetValue(value);
960 if (this.IsDefaultValue(field, fieldValue))
961 {
962 continue;
963 }
964
965 if (appendDelim)
966 {
967 this.writer.Write(JsonReader.OperatorValueDelim);
968 this.WriteLine();
969 }
970 else
971 {
972 appendDelim = true;
973 }
974
975 string fieldName = JsonNameAttribute.GetJsonName(field);
976 if (String.IsNullOrEmpty(fieldName))
977 {
978 fieldName = field.Name;
979 }
980
981 // use Attributes here to control naming
982 this.Write(fieldName);
983 this.writer.Write(JsonReader.OperatorNameDelim);
984 this.Write(fieldValue, true);
985 }
986 }
987 finally
988 {
989 this.depth--;
990 }
991
992 if (appendDelim)
993 {
994 this.WriteLine();
995 }
996 this.writer.Write(JsonReader.OperatorObjectEnd);
997 }
998
999 protected virtual void WriteLine()
1000 {
1001 if (!this.prettyPrint)
1002 {
1003 return;
1004 }
1005
1006 this.writer.WriteLine();
1007 for (int i=0; i<this.depth; i++)
1008 {
1009 this.writer.Write(this.tab);
1010 }
1011 }
1012
1013 #endregion Writer Methods
1014
1015 #region Private Methods
1016
1017 /// <summary>
1018 /// Determines if the property or field should not be serialized.
1019 /// </summary>
1020 /// <param name="objType"></param>
1021 /// <param name="member"></param>
1022 /// <param name="value"></param>
1023 /// <returns></returns>
1024 /// <remarks>
1025 /// Checks these in order, if any returns true then this is true:
1026 /// - is flagged with the JsonIgnoreAttribute property
1027 /// - has a JsonSpecifiedProperty which returns false
1028 /// </remarks>
1029 private bool IsIgnored(Type objType, MemberInfo member, object obj)
1030 {
1031 if (JsonIgnoreAttribute.IsJsonIgnore(member))
1032 {
1033 return true;
1034 }
1035
1036 string specifiedProperty = JsonSpecifiedPropertyAttribute.GetJsonSpecifiedProperty(member);
1037 if (!String.IsNullOrEmpty(specifiedProperty))
1038 {
1039 PropertyInfo specProp = objType.GetProperty(specifiedProperty);
1040 if (specProp != null)
1041 {
1042 object isSpecified = specProp.GetValue(obj, null);
1043 if (isSpecified is Boolean && !Convert.ToBoolean(isSpecified))
1044 {
1045 return true;
1046 }
1047 }
1048 }
1049
1050 if (this.UseXmlSerializationAttributes)
1051 {
1052 if (JsonIgnoreAttribute.IsXmlIgnore(member))
1053 {
1054 return true;
1055 }
1056
1057 PropertyInfo specProp = objType.GetProperty(member.Name+"Specified");
1058 if (specProp != null)
1059 {
1060 object isSpecified = specProp.GetValue(obj, null);
1061 if (isSpecified is Boolean && !Convert.ToBoolean(isSpecified))
1062 {
1063 return true;
1064 }
1065 }
1066 }
1067
1068 return false;
1069 }
1070
1071 /// <summary>
1072 /// Determines if the member value matches the DefaultValue attribute
1073 /// </summary>
1074 /// <returns>if has a value equivalent to the DefaultValueAttribute</returns>
1075 private bool IsDefaultValue(MemberInfo member, object value)
1076 {
1077 DefaultValueAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DefaultValueAttribute)) as DefaultValueAttribute;
1078 if (attribute == null)
1079 {
1080 return false;
1081 }
1082
1083 if (attribute.Value == null)
1084 {
1085 return (value == null);
1086 }
1087
1088 return (attribute.Value.Equals(value));
1089 }
1090
1091 #region GetFlagList
1092
1093 /// <summary>
1094 /// Splits a bitwise-OR'd set of enums into a list.
1095 /// </summary>
1096 /// <param name="enumType">the enum type</param>
1097 /// <param name="value">the combined value</param>
1098 /// <returns>list of flag enums</returns>
1099 /// <remarks>
1100 /// from PseudoCode.EnumHelper
1101 /// </remarks>
1102 private static Enum[] GetFlagList(Type enumType, object value)
1103 {
1104 ulong longVal = Convert.ToUInt64(value);
1105 Array enumValues = Enum.GetValues(enumType);
1106
1107 List<Enum> enums = new List<Enum>(enumValues.Length);
1108
1109 // check for empty
1110 if (longVal == 0L)
1111 {
1112 // Return the value of empty, or zero if none exists
1113 if (Convert.ToUInt64(enumValues.GetValue(0)) == 0L)
1114 enums.Add(enumValues.GetValue(0) as Enum);
1115 else
1116 enums.Add(null);
1117 return enums.ToArray();
1118 }
1119
1120 for (int i = enumValues.Length-1; i >= 0; i--)
1121 {
1122 ulong enumValue = Convert.ToUInt64(enumValues.GetValue(i));
1123
1124 if ((i == 0) && (enumValue == 0L))
1125 continue;
1126
1127 // matches a value in enumeration
1128 if ((longVal & enumValue) == enumValue)
1129 {
1130 // remove from val
1131 longVal -= enumValue;
1132
1133 // add enum to list
1134 enums.Add(enumValues.GetValue(i) as Enum);
1135 }
1136 }
1137
1138 if (longVal != 0x0L)
1139 enums.Add(Enum.ToObject(enumType, longVal) as Enum);
1140
1141 return enums.ToArray();
1142 }
1143
1144 #endregion GetFlagList
1145
1146 #endregion Private Methods
1147
1148 #region Utility Methods
1149
1150 /// <summary>
1151 /// Verifies is a valid EcmaScript variable expression.
1152 /// </summary>
1153 /// <param name="varExpr">the variable expression</param>
1154 /// <returns>varExpr</returns>
1155 public static string EnsureValidIdentifier(string varExpr, bool nested)
1156 {
1157 return JsonWriter.EnsureValidIdentifier(varExpr, nested, true);
1158 }
1159
1160 /// <summary>
1161 /// Verifies is a valid EcmaScript variable expression.
1162 /// </summary>
1163 /// <param name="varExpr">the variable expression</param>
1164 /// <returns>varExpr</returns>
1165 /// <remarks>
1166 /// http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
1167 ///
1168 /// IdentifierName =
1169 /// IdentifierStart | IdentifierName IdentifierPart
1170 /// IdentifierStart =
1171 /// Letter | '$' | '_'
1172 /// IdentifierPart =
1173 /// IdentifierStart | Digit
1174 /// </remarks>
1175 public static string EnsureValidIdentifier(string varExpr, bool nested, bool throwOnEmpty)
1176 {
1177 if (String.IsNullOrEmpty(varExpr))
1178 {
1179 if (throwOnEmpty)
1180 {
1181 throw new ArgumentException("Variable expression is empty.");
1182 }
1183 return String.Empty;
1184 }
1185
1186 varExpr = varExpr.Replace(" ", "");
1187
1188 bool indentPart = false;
1189
1190 // TODO: ensure not a keyword
1191 foreach (char ch in varExpr)
1192 {
1193 if (indentPart)
1194 {
1195 if (ch == '.' && nested)
1196 {
1197 // reset to IndentifierStart
1198 indentPart = false;
1199 continue;
1200 }
1201
1202 if (Char.IsDigit(ch))
1203 {
1204 continue;
1205 }
1206 }
1207
1208 // can be start or part
1209 if (Char.IsLetterOrDigit(ch) || ch == '_' || ch == '$'||ch=='-')
1210 {
1211 indentPart = true;
1212 continue;
1213 }
1214
1215 throw new ArgumentException("Variable expression \""+varExpr+"\" is not supported.");
1216 }
1217
1218 return varExpr;
1219 }
1220
1221 #endregion Utility Methods
1222
1223 #region IDisposable Members
1224
1225 void IDisposable.Dispose()
1226 {
1227 if (this.writer != null)
1228 {
1229 this.writer.Dispose();
1230 }
1231 }
1232
1233 #endregion IDisposable Members
1234 }
1235}
Note: See TracBrowser for help on using the repository browser.