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));
83 return isNull ? null : reader.ReadElementContentAsString ();
85 return reader.ReadElementContentAsFloat ();
87 return reader.ReadElementContentAsDouble ();
88 case TypeCode.Decimal:
89 return reader.ReadElementContentAsDecimal ();
96 int i = reader.ReadElementContentAsInt ();
98 return Enum.ToObject (type, (object)i);
100 return Convert.ChangeType (i, type, null);
102 case TypeCode.UInt64:
103 long l = reader.ReadElementContentAsLong ();
105 return Enum.ToObject (type, (object)l);
107 return Convert.ChangeType (l, type, null);
108 case TypeCode.Boolean:
109 return reader.ReadElementContentAsBoolean ();
111 if (type == typeof (Guid)) {
112 return new Guid (reader.ReadElementContentAsString ());
113 } else if (type == typeof (Uri)) {
114 return isNull ? null : new Uri (reader.ReadElementContentAsString ());
115 } else if (type == typeof (XmlQualifiedName)) {
116 string s = reader.ReadElementContentAsString ();
117 int idx = s.IndexOf (':');
118 return idx < 0 ? new XmlQualifiedName (s) : new XmlQualifiedName (s.Substring (0, idx), s.Substring (idx + 1));
119 } else if (type != typeof (object)) {
120 // strongly-typed object
121 if (reader.IsEmptyElement) {
122 // empty -> null array or object
127 Type ct = GetCollectionType (type);
129 return DeserializeGenericCollection (type, ct);
131 TypeMap map = GetTypeMap (type);
132 return map.Deserialize (this);
136 return ReadInstanceDrivenObject ();
140 Type GetRuntimeType (string name)
142 name = ToRuntimeTypeName (name);
143 if (serializer.KnownTypes != null)
144 foreach (Type t in serializer.KnownTypes)
145 if (t.FullName == name)
147 var ret = root_type.Assembly.GetType (name, false) ?? Type.GetType (name, false);
150 #if !NET_2_1 // how to do that in ML?
151 // We probably have to iterate all the existing
152 // assemblies that are loaded in current domain.
153 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
154 ret = ass.GetType (name, false);
162 object ReadInstanceDrivenObject ()
164 string type = reader.GetAttribute ("type");
165 if (type == "object") {
166 string runtimeType = reader.GetAttribute ("__type");
167 if (runtimeType != null) {
168 Type t = GetRuntimeType (runtimeType);
170 throw SerializationError (String.Format ("Cannot load type '{0}'", runtimeType));
171 return ReadObject (t);
174 string v = reader.ReadElementContentAsString ();
183 throw SerializationError (String.Format ("Invalid JSON boolean value: {0}", v));
189 throw SerializationError (String.Format ("Invalid JSON null value: {0}", v));
193 if (int.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out i))
196 if (long.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out l))
199 if (ulong.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out ul))
202 if (double.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dbl))
205 if (decimal.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dec))
207 throw SerializationError (String.Format ("Invalid JSON input: {0}", v));
209 throw SerializationError (String.Format ("Unexpected type: {0}", type));
213 string FormatTypeName (Type type)
215 return type.Namespace == null ? type.Name : String.Format ("{0}:#{1}", type.Name, type.Namespace);
218 string ToRuntimeTypeName (string s)
220 int idx = s.IndexOf (":#", StringComparison.Ordinal);
221 return idx < 0 ? s : String.Concat (s.Substring (idx + 2), ".", s.Substring (0, idx));
224 Type GetCollectionType (Type type)
227 return type.GetElementType ();
228 if (type.IsGenericType) {
229 // returns T for ICollection<T>
230 Type [] ifaces = type.GetInterfaces ();
231 foreach (Type i in ifaces)
232 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (ICollection<>)))
233 return i.GetGenericArguments () [0];
235 if (typeof (IList).IsAssignableFrom (type))
236 // return typeof(object) for mere collection.
237 return typeof (object);
242 object DeserializeGenericCollection (Type collectionType, Type elementType)
244 reader.ReadStartElement ();
246 if (typeof (IList).IsAssignableFrom (collectionType)) {
248 Type listType = collectionType.IsArray ? typeof (List<>).MakeGenericType (elementType) : null;
250 Type listType = collectionType.IsArray ? typeof (ArrayList) : null;
252 IList c = (IList) Activator.CreateInstance (listType ?? collectionType);
253 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
254 if (!reader.IsStartElement ("item"))
255 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
256 Type et = elementType == typeof (object) || elementType.IsAbstract ? null : elementType;
257 object elem = ReadObject (et ?? typeof (object));
261 if (collectionType.IsArray) {
262 Array array = Array.CreateInstance (elementType, c.Count);
269 ret = collectionType.IsArray ? ((ArrayList) c).ToArray (elementType) : c;
272 object c = Activator.CreateInstance (collectionType);
273 MethodInfo add = collectionType.GetMethod ("Add", new Type [] {elementType});
275 var icoll = typeof (ICollection<>).MakeGenericType (elementType);
276 if (icoll.IsAssignableFrom (c.GetType ()))
277 add = icoll.GetMethod ("Add");
280 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
281 if (!reader.IsStartElement ("item"))
282 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
283 object elem = ReadObject (elementType);
284 add.Invoke (c, new object [] {elem});
289 reader.ReadEndElement ();
293 TypeMap GetTypeMap (Type type)
296 if (!typemaps.TryGetValue (type, out map)) {
297 map = TypeMap.CreateTypeMap (type);
298 typemaps [type] = map;
303 Exception SerializationError (string basemsg)
305 IXmlLineInfo li = reader as IXmlLineInfo;
306 if (li == null || !li.HasLineInfo ())
307 return new SerializationException (basemsg);
309 return new SerializationException (String.Format ("{0}. Error at {1} ({2},{3})", basemsg, reader.BaseURI, li.LineNumber, li.LinePosition));