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;
34 using System.Reflection;
38 namespace System.Runtime.Serialization.Json
40 class JsonSerializationReader
42 DataContractJsonSerializer serializer;
44 int serialized_object_count;
45 bool verify_object_name;
46 Dictionary<Type, TypeMap> typemaps = new Dictionary<Type, TypeMap> ();
49 public JsonSerializationReader (DataContractJsonSerializer serializer, XmlReader reader, Type rootType, bool verifyObjectName)
51 this.serializer = serializer;
53 this.root_type = rootType;
54 this.verify_object_name = verifyObjectName;
57 public XmlReader Reader {
58 get { return reader; }
61 public object ReadRoot ()
63 TypeMap rootMap = GetTypeMap (root_type);
65 object v = ReadObject (root_type);
69 public object ReadObject (Type type)
71 if (serialized_object_count ++ == serializer.MaxItemsInObjectGraph)
72 throw SerializationError (String.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer.MaxItemsInObjectGraph));
74 bool isNull = reader.GetAttribute ("type") == "null";
76 switch (Type.GetTypeCode (type)) {
78 string dbn = reader.ReadElementContentAsString ();
79 if (dbn != String.Empty)
80 throw new SerializationException (String.Format ("The only expected DBNull value string is '{{}}'. Tha actual input was '{0}'.", dbn));
84 reader.ReadElementContentAsString ();
88 return reader.ReadElementContentAsString ();
90 return reader.ReadElementContentAsFloat ();
92 return reader.ReadElementContentAsDouble ();
93 case TypeCode.Decimal:
94 return reader.ReadElementContentAsDecimal ();
100 case TypeCode.UInt32:
101 int i = reader.ReadElementContentAsInt ();
103 return Enum.ToObject (type, (object)i);
105 return Convert.ChangeType (i, type, null);
107 case TypeCode.UInt64:
108 long l = reader.ReadElementContentAsLong ();
110 return Enum.ToObject (type, (object)l);
112 return Convert.ChangeType (l, type, null);
113 case TypeCode.Boolean:
114 return reader.ReadElementContentAsBoolean ();
115 case TypeCode.DateTime:
116 // it does not use ReadElementContentAsDateTime(). Different string format.
117 var s = reader.ReadElementContentAsString ();
118 if (s.Length < 2 || !s.StartsWith ("/Date(", StringComparison.Ordinal) || !s.EndsWith (")/", StringComparison.Ordinal))
119 throw new XmlException ("Invalid JSON DateTime format. The value format should be '/Date(UnixTime)/'");
120 return new DateTime (1970, 1, 1).AddMilliseconds (long.Parse (s.Substring (6, s.Length - 8)));
122 if (type == typeof (Guid)) {
123 return new Guid (reader.ReadElementContentAsString ());
124 } else if (type == typeof (Uri)) {
126 reader.ReadElementContentAsString ();
130 return new Uri (reader.ReadElementContentAsString ());
131 } else if (type == typeof (XmlQualifiedName)) {
132 s = reader.ReadElementContentAsString ();
133 int idx = s.IndexOf (':');
134 return idx < 0 ? new XmlQualifiedName (s) : new XmlQualifiedName (s.Substring (0, idx), s.Substring (idx + 1));
135 } else if (type != typeof (object)) {
136 // strongly-typed object
137 if (reader.IsEmptyElement) {
138 // empty -> null array or object
143 Type ct = GetCollectionType (type);
145 return DeserializeGenericCollection (type, ct);
147 TypeMap map = GetTypeMap (type);
148 return map.Deserialize (this);
152 return ReadInstanceDrivenObject ();
156 Type GetRuntimeType (string name)
158 name = ToRuntimeTypeName (name);
159 if (serializer.KnownTypes != null)
160 foreach (Type t in serializer.KnownTypes)
161 if (t.FullName == name)
163 var ret = root_type.Assembly.GetType (name, false) ?? Type.GetType (name, false);
166 #if !NET_2_1 // how to do that in ML?
167 // We probably have to iterate all the existing
168 // assemblies that are loaded in current domain.
169 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
170 ret = ass.GetType (name, false);
178 object ReadInstanceDrivenObject ()
180 string type = reader.GetAttribute ("type");
181 if (type == "object") {
182 string runtimeType = reader.GetAttribute ("__type");
183 if (runtimeType != null) {
184 Type t = GetRuntimeType (runtimeType);
186 throw SerializationError (String.Format ("Cannot load type '{0}'", runtimeType));
187 return ReadObject (t);
190 string v = reader.ReadElementContentAsString ();
199 throw SerializationError (String.Format ("Invalid JSON boolean value: {0}", v));
205 throw SerializationError (String.Format ("Invalid JSON null value: {0}", v));
209 if (int.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out i))
212 if (long.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out l))
215 if (ulong.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out ul))
218 if (double.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dbl))
221 if (decimal.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dec))
223 throw SerializationError (String.Format ("Invalid JSON input: {0}", v));
225 throw SerializationError (String.Format ("Unexpected type: {0}", type));
229 string FormatTypeName (Type type)
231 return type.Namespace == null ? type.Name : String.Format ("{0}:#{1}", type.Name, type.Namespace);
234 string ToRuntimeTypeName (string s)
236 int idx = s.IndexOf (":#", StringComparison.Ordinal);
237 return idx < 0 ? s : String.Concat (s.Substring (idx + 2), ".", s.Substring (0, idx));
240 Type GetCollectionType (Type type)
243 return type.GetElementType ();
244 if (type.IsGenericType) {
245 // returns T for ICollection<T>
246 Type [] ifaces = type.GetInterfaces ();
247 foreach (Type i in ifaces)
248 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (ICollection<>)))
249 return i.GetGenericArguments () [0];
251 if (typeof (IList).IsAssignableFrom (type))
252 // return typeof(object) for mere collection.
253 return typeof (object);
258 object DeserializeGenericCollection (Type collectionType, Type elementType)
260 reader.ReadStartElement ();
262 if (typeof (IList).IsAssignableFrom (collectionType)) {
264 Type listType = collectionType.IsArray ? typeof (List<>).MakeGenericType (elementType) : null;
266 Type listType = collectionType.IsArray ? typeof (ArrayList) : null;
268 IList c = (IList) Activator.CreateInstance (listType ?? collectionType);
269 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
270 if (!reader.IsStartElement ("item"))
271 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
272 Type et = elementType == typeof (object) || elementType.IsAbstract ? null : elementType;
273 object elem = ReadObject (et ?? typeof (object));
277 if (collectionType.IsArray) {
278 Array array = Array.CreateInstance (elementType, c.Count);
285 ret = collectionType.IsArray ? ((ArrayList) c).ToArray (elementType) : c;
288 object c = Activator.CreateInstance (collectionType);
289 MethodInfo add = collectionType.GetMethod ("Add", new Type [] {elementType});
291 var icoll = typeof (ICollection<>).MakeGenericType (elementType);
292 if (icoll.IsAssignableFrom (c.GetType ()))
293 add = icoll.GetMethod ("Add");
296 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
297 if (!reader.IsStartElement ("item"))
298 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
299 object elem = ReadObject (elementType);
300 add.Invoke (c, new object [] {elem});
305 reader.ReadEndElement ();
309 TypeMap GetTypeMap (Type type)
312 if (!typemaps.TryGetValue (type, out map)) {
313 map = TypeMap.CreateTypeMap (type);
314 typemaps [type] = map;
319 Exception SerializationError (string basemsg)
321 IXmlLineInfo li = reader as IXmlLineInfo;
322 if (li == null || !li.HasLineInfo ())
323 return new SerializationException (basemsg);
325 return new SerializationException (String.Format ("{0}. Error at {1} ({2},{3})", basemsg, reader.BaseURI, li.LineNumber, li.LinePosition));