2
0

JsonSerializationWriter.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // JsonSerializationWriter.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Collections;
  30. using System.Collections.Generic;
  31. using System.Collections.ObjectModel;
  32. using System.Globalization;
  33. using System.IO;
  34. using System.Reflection;
  35. using System.Text;
  36. using System.Xml;
  37. namespace System.Runtime.Serialization.Json
  38. {
  39. class JsonSerializationWriter
  40. {
  41. DataContractJsonSerializer serializer;
  42. XmlWriter writer;
  43. int serialized_object_count;
  44. bool always_emit_type;
  45. Dictionary<Type, TypeMap> typemaps = new Dictionary<Type, TypeMap> ();
  46. Type root_type;
  47. public JsonSerializationWriter (DataContractJsonSerializer serializer, XmlWriter writer, Type rootType, bool alwaysEmitTypeInformation)
  48. {
  49. this.serializer = serializer;
  50. this.writer = writer;
  51. this.root_type = rootType;
  52. this.always_emit_type = alwaysEmitTypeInformation;
  53. }
  54. public XmlWriter Writer {
  55. get { return writer; }
  56. }
  57. public void WriteObjectContent (object graph, bool top, bool outputTypeName)
  58. {
  59. if (graph == null) {
  60. if (top)
  61. GetTypeMap (root_type); // to make sure to reject invalid contracts
  62. writer.WriteAttributeString ("type", "null");
  63. writer.WriteString (null);
  64. return;
  65. }
  66. if (serialized_object_count ++ == serializer.MaxItemsInObjectGraph)
  67. throw new SerializationException (String.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer.MaxItemsInObjectGraph));
  68. var type = graph.GetType ();
  69. if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
  70. type = type.GetGenericArguments () [0];
  71. switch (Type.GetTypeCode (type)) {
  72. case TypeCode.Char:
  73. case TypeCode.String:
  74. writer.WriteString (graph.ToString ());
  75. break;
  76. case TypeCode.Single:
  77. case TypeCode.Double:
  78. writer.WriteAttributeString ("type", "number");
  79. writer.WriteString (((IFormattable) graph).ToString ("R", CultureInfo.InvariantCulture));
  80. break;
  81. case TypeCode.Byte:
  82. case TypeCode.SByte:
  83. case TypeCode.Int16:
  84. case TypeCode.Int32:
  85. case TypeCode.Int64:
  86. case TypeCode.UInt16:
  87. case TypeCode.UInt32:
  88. case TypeCode.UInt64:
  89. case TypeCode.Decimal:
  90. writer.WriteAttributeString ("type", "number");
  91. if (type.IsEnum)
  92. graph = ((IConvertible) graph).ToType (Enum.GetUnderlyingType (type), CultureInfo.InvariantCulture);
  93. writer.WriteString (((IFormattable) graph).ToString ("G", CultureInfo.InvariantCulture));
  94. break;
  95. case TypeCode.Boolean:
  96. writer.WriteAttributeString ("type", "boolean");
  97. if ((bool) graph)
  98. writer.WriteString ("true");
  99. else
  100. writer.WriteString ("false");
  101. break;
  102. case TypeCode.DateTime:
  103. writer.WriteString (String.Format (CultureInfo.InvariantCulture, "/Date({0})/", (long) ((DateTime) graph).Subtract (new DateTime (1970, 1, 1)).TotalMilliseconds));
  104. break;
  105. default:
  106. if (graph is Guid) {
  107. goto case TypeCode.String;
  108. } else if (graph is Uri) {
  109. goto case TypeCode.String;
  110. } else if (graph is XmlQualifiedName) {
  111. XmlQualifiedName qn = (XmlQualifiedName) graph;
  112. writer.WriteString (qn.Name);
  113. writer.WriteString (":");
  114. writer.WriteString (qn.Namespace);
  115. } else if (TypeMap.IsDictionary (type)) {
  116. writer.WriteAttributeString ("type", "array");
  117. bool otn = !(graph is Array && type.GetElementType () != typeof (object));
  118. var d = graph as IDictionary;
  119. if (d != null) {
  120. // Optimize the IDictionary case to avoid reflection
  121. foreach (object k in d.Keys)
  122. WriteItem (k, d [k], otn);
  123. } else {
  124. // we can't typecast to IDictionary<,> and can't use dynamic for iOS support
  125. var itemGetter = GetDictionaryProperty (type, "Item");
  126. var keysGetter = GetDictionaryProperty (type, "Keys");
  127. var argarr = new object [1];
  128. foreach (object o in (IEnumerable) keysGetter.GetValue (graph, null)) {
  129. argarr [0] = o;
  130. WriteItem (o, itemGetter.GetValue (graph, argarr), otn);
  131. }
  132. }
  133. } else if (graph is Array || TypeMap.IsEnumerable (type)) {
  134. writer.WriteAttributeString ("type", "array");
  135. foreach (object o in (IEnumerable) graph) {
  136. writer.WriteStartElement ("item");
  137. // when it is typed, then no need to output "__type"
  138. WriteObjectContent (o, false, !(graph is Array && type.GetElementType () != typeof (object)));
  139. writer.WriteEndElement ();
  140. }
  141. } else { // object
  142. TypeMap tm = GetTypeMap (type);
  143. if (tm != null) {
  144. // FIXME: I'm not sure how it is determined whether __type is written or not...
  145. if (outputTypeName || always_emit_type)
  146. writer.WriteAttributeString ("__type", FormatTypeName (type));
  147. tm.Serialize (this, graph, "object");
  148. }
  149. else
  150. // it does not emit type="object" (as the graph is regarded as a string)
  151. // writer.WriteString (graph.ToString ());
  152. throw new InvalidDataContractException (String.Format ("Type {0} cannot be serialized by this JSON serializer", type));
  153. }
  154. break;
  155. }
  156. }
  157. void WriteItem (object key, object value, bool outputTypeName)
  158. {
  159. writer.WriteStartElement ("item");
  160. writer.WriteAttributeString ("type", "object");
  161. // outputting a KeyValuePair as <Key .. /><Value ... />
  162. writer.WriteStartElement ("Key");
  163. WriteObjectContent (key, false, outputTypeName);
  164. writer.WriteEndElement ();
  165. writer.WriteStartElement ("Value");
  166. WriteObjectContent (value, false, outputTypeName);
  167. writer.WriteEndElement ();
  168. writer.WriteEndElement ();
  169. }
  170. PropertyInfo GetDictionaryProperty (Type type, string propertyName)
  171. {
  172. var p = type.GetProperty (propertyName);
  173. if (p != null)
  174. return p;
  175. // check explicit - but the generic names might differ, e.g. TKey,TValue vs T,V
  176. var ap = type.GetProperties (BindingFlags.Instance | BindingFlags.NonPublic);
  177. foreach (var cp in ap) {
  178. if (!cp.Name.EndsWith (propertyName, StringComparison.Ordinal))
  179. continue;
  180. if (cp.Name.StartsWith ("System.Collections.Generic.IDictionary<", StringComparison.Ordinal))
  181. return cp;
  182. }
  183. return null;
  184. }
  185. string FormatTypeName (Type type)
  186. {
  187. return String.Format ("{0}:#{1}", type.Name, type.Namespace);
  188. }
  189. TypeMap GetTypeMap (Type type)
  190. {
  191. TypeMap map;
  192. if (!typemaps.TryGetValue (type, out map)) {
  193. map = TypeMap.CreateTypeMap (type);
  194. typemaps [type] = map;
  195. }
  196. return map;
  197. }
  198. }
  199. }