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 case TypeCode.Double:
103 case TypeCode.Decimal:
104 return ReadValueType (type, nullable);
109 case TypeCode.UInt16:
110 case TypeCode.UInt32:
113 return Enum.ToObject (type, Convert.ChangeType (reader.ReadElementContentAsLong (), Enum.GetUnderlyingType (type), null));
115 return ReadValueType (type, nullable);
116 case TypeCode.UInt64:
118 return Enum.ToObject (type, Convert.ChangeType (reader.ReadElementContentAsDecimal (), Enum.GetUnderlyingType (type), null));
120 return ReadValueType (type, nullable);
121 case TypeCode.Boolean:
122 return ReadValueType (type, nullable);
123 case TypeCode.DateTime:
124 // it does not use ReadElementContentAsDateTime(). Different string format.
125 var s = reader.ReadElementContentAsString ();
126 if (s.Length < 2 || !s.StartsWith ("/Date(", StringComparison.Ordinal) || !s.EndsWith (")/", StringComparison.Ordinal)) {
129 throw new XmlException ("Invalid JSON DateTime format. The value format should be '/Date(UnixTime)/'");
132 // The date can contain [SIGN]LONG, [SIGN]LONG+HOURSMINUTES or [SIGN]LONG-HOURSMINUTES
133 // the format for HOURSMINUTES is DDDD
134 int tidx = s.IndexOf ('-', 8);
136 tidx = s.IndexOf ('+', 8);
139 s = s.Substring (6, s.Length - 8);
142 int.TryParse (s.Substring (tidx+1, s.Length-3-tidx), out offset);
144 minutes = (offset % 100) + (offset / 100) * 60;
148 s = s.Substring (6, tidx-6);
150 var date = new DateTime (1970, 1, 1).AddMilliseconds (long.Parse (s));
152 date = date.AddMinutes (minutes);
155 if (type == typeof (Guid)) {
156 return new Guid (reader.ReadElementContentAsString ());
157 } else if (type == typeof (Uri)) {
159 reader.ReadElementContentAsString ();
163 return new Uri (reader.ReadElementContentAsString (), UriKind.RelativeOrAbsolute);
164 } else if (type == typeof (XmlQualifiedName)) {
165 s = reader.ReadElementContentAsString ();
166 int idx = s.IndexOf (':');
167 return idx < 0 ? new XmlQualifiedName (s) : new XmlQualifiedName (s.Substring (0, idx), s.Substring (idx + 1));
168 } else if (type != typeof (object)) {
169 // strongly-typed object
170 if (reader.IsEmptyElement) {
171 // empty -> null array or object
176 Type ct = GetCollectionElementType (type);
178 return DeserializeGenericCollection (type, ct);
180 TypeMap map = GetTypeMap (type);
181 return map.Deserialize (this);
185 return ReadInstanceDrivenObject ();
189 object ReadValueType (Type type, bool nullable)
191 string s = reader.ReadElementContentAsString ();
192 return nullable && s.Trim ().Length == 0 ? null : Convert.ChangeType (s, type, null);
196 Type GetRuntimeType (string name)
198 name = ToRuntimeTypeName (name);
199 if (serializer.KnownTypes != null)
200 foreach (Type t in serializer.KnownTypes)
201 if (t.FullName == name)
203 var ret = root_type.Assembly.GetType (name, false) ?? Type.GetType (name, false);
207 // We probably have to iterate all the existing
208 // assemblies that are loaded in current domain.
209 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
210 ret = ass.GetType (name, false);
218 object ReadInstanceDrivenObject ()
220 string type = reader.GetAttribute ("type");
226 string runtimeType = reader.GetAttribute ("__type");
227 if (runtimeType != null) {
228 Type t = GetRuntimeType (runtimeType);
230 throw SerializationError (String.Format ("Cannot load type '{0}'", runtimeType));
231 return ReadObject (t);
235 string v = reader.ReadElementContentAsString ();
244 throw SerializationError (String.Format ("Invalid JSON boolean value: {0}", v));
250 if (int.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out i))
253 if (long.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out l))
256 if (ulong.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out ul))
259 if (double.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dbl))
262 if (decimal.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dec))
264 throw SerializationError (String.Format ("Invalid JSON input: {0}", v));
266 throw SerializationError (String.Format ("Unexpected type: {0}", type));
270 string FormatTypeName (Type type)
272 return type.Namespace == null ? type.Name : String.Format ("{0}:#{1}", type.Name, type.Namespace);
275 string ToRuntimeTypeName (string s)
277 int idx = s.IndexOf (":#", StringComparison.Ordinal);
278 return idx < 0 ? s : String.Concat (s.Substring (idx + 2), ".", s.Substring (0, idx));
281 IEnumerable<Type> GetInterfaces2 (Type type)
283 if (type.IsInterface)
285 foreach (var t in type.GetInterfaces ())
289 Type GetCollectionElementType (Type type)
292 return type.GetElementType ();
293 if (type.IsGenericType) {
294 // returns T for IEnumerable<T>
295 foreach (Type i in GetInterfaces2 (type))
296 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>)))
297 return i.GetGenericArguments () [0];
299 if (typeof (IEnumerable).IsAssignableFrom (type))
300 // return typeof(object) for mere collection.
301 return typeof (object);
306 object DeserializeGenericCollection (Type collectionType, Type elementType)
308 reader.ReadStartElement ();
310 if (collectionType.IsInterface)
311 collectionType = typeof (List<>).MakeGenericType (elementType);
312 if (TypeMap.IsDictionary (collectionType)) {
313 object dic = Activator.CreateInstance (collectionType);
314 var itemSetter = dic.GetType ().GetProperty ("Item");
315 var keyarr = new object [1];
317 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
318 if (!reader.IsStartElement ("item"))
319 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
321 // reading a KeyValuePair in the form of <Key .../><Value .../>
323 reader.MoveToContent ();
324 object key = ReadObject (elementType.GetGenericArguments () [0]);
325 reader.MoveToContent ();
326 object val = ReadObject (elementType.GetGenericArguments () [1]);
329 itemSetter.SetValue (dic, val, keyarr);
332 } else if (typeof (IList).IsAssignableFrom (collectionType)) {
334 Type listType = collectionType.IsArray ? typeof (List<>).MakeGenericType (elementType) : null;
336 Type listType = collectionType.IsArray ? typeof (ArrayList) : null;
338 IList c = (IList) Activator.CreateInstance (listType ?? collectionType);
339 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
340 if (!reader.IsStartElement ("item"))
341 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
342 Type et = elementType == typeof (object) || elementType.IsAbstract ? null : elementType;
343 object elem = ReadObject (et ?? typeof (object));
347 if (collectionType.IsArray) {
348 Array array = Array.CreateInstance (elementType, c.Count);
355 ret = collectionType.IsArray ? ((ArrayList) c).ToArray (elementType) : c;
358 object c = Activator.CreateInstance (collectionType);
359 MethodInfo add = collectionType.GetMethod ("Add", new Type [] {elementType});
361 var icoll = typeof (ICollection<>).MakeGenericType (elementType);
362 if (icoll.IsAssignableFrom (c.GetType ()))
363 add = icoll.GetMethod ("Add");
366 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
367 if (!reader.IsStartElement ("item"))
368 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
369 object elem = ReadObject (elementType);
370 add.Invoke (c, new object [] {elem});
375 reader.ReadEndElement ();
379 TypeMap GetTypeMap (Type type)
382 if (!typemaps.TryGetValue (type, out map)) {
383 map = TypeMap.CreateTypeMap (type);
384 typemaps [type] = map;
389 Exception SerializationError (string basemsg)
391 IXmlLineInfo li = reader as IXmlLineInfo;
392 if (li == null || !li.HasLineInfo ())
393 return new SerializationException (basemsg);
395 return new SerializationException (String.Format ("{0}. Error at {1} ({2},{3})", basemsg, reader.BaseURI, li.LineNumber, li.LinePosition));