2 // JsonSerializationReader.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
32 using System.Globalization;
35 using System.Reflection;
39 namespace System.Runtime.Serialization.Json
41 class JsonSerializationReader
43 DataContractJsonSerializer serializer;
45 int serialized_object_count;
46 bool verify_object_name;
47 Dictionary<Type, TypeMap> typemaps = new Dictionary<Type, TypeMap> ();
50 public JsonSerializationReader (DataContractJsonSerializer serializer, XmlReader reader, Type rootType, bool verifyObjectName)
52 this.serializer = serializer;
54 this.root_type = rootType;
55 this.verify_object_name = verifyObjectName;
58 public XmlReader Reader {
59 get { return reader; }
62 public object ReadRoot ()
64 TypeMap rootMap = GetTypeMap (root_type);
66 object v = ReadObject (root_type);
70 public object ReadObject (Type type)
72 if (serialized_object_count ++ == serializer.MaxItemsInObjectGraph)
73 throw SerializationError (String.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer.MaxItemsInObjectGraph));
75 bool nullable = false;
76 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>)) {
78 type = Nullable.GetUnderlyingType (type);
81 bool isNull = reader.GetAttribute ("type") == "null";
83 switch (Type.GetTypeCode (type)) {
85 string dbn = reader.ReadElementContentAsString ();
86 if (dbn != String.Empty)
87 throw new SerializationException (String.Format ("The only expected DBNull value string is '{{}}'. Tha actual input was '{0}'.", dbn));
91 reader.ReadElementContentAsString ();
95 return reader.ReadElementContentAsString ();
97 var c = reader.ReadElementContentAsString ();
99 throw new XmlException ("Invalid JSON char");
100 return Char.Parse(c);
101 case TypeCode.Single:
102 return reader.ReadElementContentAsFloat ();
103 case TypeCode.Double:
104 return reader.ReadElementContentAsDouble ();
105 case TypeCode.Decimal:
106 return reader.ReadElementContentAsDecimal ();
111 case TypeCode.UInt16:
113 return Enum.ToObject (type, Convert.ChangeType (reader.ReadElementContentAsLong (), Enum.GetUnderlyingType (type), null));
115 return Convert.ChangeType (reader.ReadElementContentAsDecimal (), type, null);
116 case TypeCode.UInt32:
118 case TypeCode.UInt64:
120 return Enum.ToObject (type, Convert.ChangeType (reader.ReadElementContentAsLong (), Enum.GetUnderlyingType (type), null));
122 return Convert.ChangeType (reader.ReadElementContentAsDecimal (), type, null);
123 case TypeCode.Boolean:
124 return reader.ReadElementContentAsBoolean ();
125 case TypeCode.DateTime:
126 // it does not use ReadElementContentAsDateTime(). Different string format.
127 var s = reader.ReadElementContentAsString ();
128 if (s.Length < 2 || !s.StartsWith ("/Date(", StringComparison.Ordinal) || !s.EndsWith (")/", StringComparison.Ordinal)) {
131 throw new XmlException ("Invalid JSON DateTime format. The value format should be '/Date(UnixTime)/'");
134 // The date can contain [SIGN]LONG, [SIGN]LONG+HOURSMINUTES or [SIGN]LONG-HOURSMINUTES
135 // the format for HOURSMINUTES is DDDD
136 int tidx = s.IndexOf ('-', 8);
138 tidx = s.IndexOf ('+', 8);
141 s = s.Substring (6, s.Length - 8);
144 int.TryParse (s.Substring (tidx+1, s.Length-3-tidx), out offset);
146 minutes = (offset % 100) + (offset / 100) * 60;
150 s = s.Substring (6, tidx-6);
152 var date = new DateTime (1970, 1, 1).AddMilliseconds (long.Parse (s));
154 date = date.AddMinutes (minutes);
157 if (type == typeof (Guid)) {
158 return new Guid (reader.ReadElementContentAsString ());
159 } else if (type == typeof (Uri)) {
161 reader.ReadElementContentAsString ();
165 return new Uri (reader.ReadElementContentAsString (), UriKind.RelativeOrAbsolute);
166 } else if (type == typeof (XmlQualifiedName)) {
167 s = reader.ReadElementContentAsString ();
168 int idx = s.IndexOf (':');
169 return idx < 0 ? new XmlQualifiedName (s) : new XmlQualifiedName (s.Substring (0, idx), s.Substring (idx + 1));
170 } else if (type != typeof (object)) {
171 // strongly-typed object
172 if (reader.IsEmptyElement) {
173 // empty -> null array or object
178 Type ct = GetCollectionElementType (type);
180 return DeserializeGenericCollection (type, ct);
182 TypeMap map = GetTypeMap (type);
183 return map.Deserialize (this);
187 return ReadInstanceDrivenObject ();
191 Type GetRuntimeType (string name)
193 name = ToRuntimeTypeName (name);
194 if (serializer.KnownTypes != null)
195 foreach (Type t in serializer.KnownTypes)
196 if (t.FullName == name)
198 var ret = root_type.Assembly.GetType (name, false) ?? Type.GetType (name, false);
202 // We probably have to iterate all the existing
203 // assemblies that are loaded in current domain.
204 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
205 ret = ass.GetType (name, false);
213 object ReadInstanceDrivenObject ()
215 string type = reader.GetAttribute ("type");
221 string runtimeType = reader.GetAttribute ("__type");
222 if (runtimeType != null) {
223 Type t = GetRuntimeType (runtimeType);
225 throw SerializationError (String.Format ("Cannot load type '{0}'", runtimeType));
226 return ReadObject (t);
230 string v = reader.ReadElementContentAsString ();
239 throw SerializationError (String.Format ("Invalid JSON boolean value: {0}", v));
245 if (int.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out i))
248 if (long.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out l))
251 if (ulong.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out ul))
254 if (double.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dbl))
257 if (decimal.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dec))
259 throw SerializationError (String.Format ("Invalid JSON input: {0}", v));
261 throw SerializationError (String.Format ("Unexpected type: {0}", type));
265 string FormatTypeName (Type type)
267 return type.Namespace == null ? type.Name : String.Format ("{0}:#{1}", type.Name, type.Namespace);
270 string ToRuntimeTypeName (string s)
272 int idx = s.IndexOf (":#", StringComparison.Ordinal);
273 return idx < 0 ? s : String.Concat (s.Substring (idx + 2), ".", s.Substring (0, idx));
276 IEnumerable<Type> GetInterfaces2 (Type type)
278 if (type.IsInterface)
280 foreach (var t in type.GetInterfaces ())
284 Type GetCollectionElementType (Type type)
287 return type.GetElementType ();
288 if (type.IsGenericType) {
289 // returns T for IEnumerable<T>
290 foreach (Type i in GetInterfaces2 (type))
291 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>)))
292 return i.GetGenericArguments () [0];
294 if (typeof (IEnumerable).IsAssignableFrom (type))
295 // return typeof(object) for mere collection.
296 return typeof (object);
301 object DeserializeGenericCollection (Type collectionType, Type elementType)
303 reader.ReadStartElement ();
305 if (collectionType.IsInterface)
306 collectionType = typeof (List<>).MakeGenericType (elementType);
307 if (TypeMap.IsDictionary (collectionType)) {
308 object dic = Activator.CreateInstance (collectionType);
309 var itemSetter = dic.GetType ().GetProperty ("Item");
310 var keyarr = new object [1];
312 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
313 if (!reader.IsStartElement ("item"))
314 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
316 // reading a KeyValuePair in the form of <Key .../><Value .../>
318 reader.MoveToContent ();
319 object key = ReadObject (elementType.GetGenericArguments () [0]);
320 reader.MoveToContent ();
321 object val = ReadObject (elementType.GetGenericArguments () [1]);
324 itemSetter.SetValue (dic, val, keyarr);
327 } else if (typeof (IList).IsAssignableFrom (collectionType)) {
329 Type listType = collectionType.IsArray ? typeof (List<>).MakeGenericType (elementType) : null;
331 Type listType = collectionType.IsArray ? typeof (ArrayList) : null;
333 IList c = (IList) Activator.CreateInstance (listType ?? collectionType);
334 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
335 if (!reader.IsStartElement ("item"))
336 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
337 Type et = elementType == typeof (object) || elementType.IsAbstract ? null : elementType;
338 object elem = ReadObject (et ?? typeof (object));
342 if (collectionType.IsArray) {
343 Array array = Array.CreateInstance (elementType, c.Count);
350 ret = collectionType.IsArray ? ((ArrayList) c).ToArray (elementType) : c;
353 object c = Activator.CreateInstance (collectionType);
354 MethodInfo add = collectionType.GetMethod ("Add", new Type [] {elementType});
356 var icoll = typeof (ICollection<>).MakeGenericType (elementType);
357 if (icoll.IsAssignableFrom (c.GetType ()))
358 add = icoll.GetMethod ("Add");
361 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
362 if (!reader.IsStartElement ("item"))
363 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
364 object elem = ReadObject (elementType);
365 add.Invoke (c, new object [] {elem});
370 reader.ReadEndElement ();
374 TypeMap GetTypeMap (Type type)
377 if (!typemaps.TryGetValue (type, out map)) {
378 map = TypeMap.CreateTypeMap (type);
379 typemaps [type] = map;
384 Exception SerializationError (string basemsg)
386 IXmlLineInfo li = reader as IXmlLineInfo;
387 if (li == null || !li.HasLineInfo ())
388 return new SerializationException (basemsg);
390 return new SerializationException (String.Format ("{0}. Error at {1} ({2},{3})", basemsg, reader.BaseURI, li.LineNumber, li.LinePosition));