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;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.Serialization;
41 namespace System.Runtime.Serialization.Json
45 static bool IsInvalidNCName (string name)
47 if (name == null || name.Length == 0)
50 XmlConvert.VerifyNCName (name);
51 } catch (XmlException) {
57 public static TypeMap CreateTypeMap (Type type)
59 object [] atts = type.GetCustomAttributes (typeof (DataContractAttribute), true);
61 return CreateTypeMap (type, (DataContractAttribute) atts [0]);
63 atts = type.GetCustomAttributes (typeof (SerializableAttribute), false);
65 return CreateTypeMap (type, null);
67 if (IsPrimitiveType (type))
70 return CreateDefaultTypeMap (type);
73 static bool IsPrimitiveType (Type type)
77 if (Type.GetTypeCode (type) != TypeCode.Object)
78 return true; // FIXME: it is likely hacky
82 static TypeMap CreateDefaultTypeMap (Type type)
84 var l = new List<TypeMapMember> ();
85 foreach (var fi in type.GetFields ())
87 l.Add (new TypeMapField (fi, null));
88 foreach (var pi in type.GetProperties ())
89 if (pi.CanRead && pi.CanWrite && !pi.GetGetMethod (true).IsStatic && pi.GetIndexParameters ().Length == 0)
90 l.Add (new TypeMapProperty (pi, null));
91 l.Sort ((x, y) => x.Order != y.Order ? x.Order - y.Order : String.Compare (x.Name, y.Name, StringComparison.Ordinal));
92 return new TypeMap (type, null, l.ToArray ());
95 internal static bool IsDictionary (Type type)
98 inter = type.GetInterface ("System.Collections.IDictionary", false);
100 && type.GetMethod ("Add", new Type[] { typeof (object), typeof (object) }) != null)
103 inter = type.GetInterface ("System.Collections.Generic.IDictionary`2", false);
105 && type.GetMethod ("Add", new Type[] { inter.GetGenericArguments() [0],
106 inter.GetGenericArguments() [1] }) != null)
111 internal static bool IsEnumerable (Type type)
113 if (type.IsGenericType &&
114 type.GetGenericTypeDefinition() == typeof (LinkedList<>))
117 if (IsPrimitiveType (type) || IsDictionary (type))
121 inter = type.GetInterface ("System.Collections.Generic.IReadOnlyCollection`1", false);
125 inter = type.GetInterface ("System.Collections.IEnumerable", false);
126 if (inter != null && type.GetMethod ("Add", new Type[] { typeof (object) }) != null)
129 inter = type.GetInterface ("System.Collections.Generic.IEnumerable`1", false);
130 if (inter != null && type.GetMethod ("Add", new Type[] { inter.GetGenericArguments() [0] }) != null)
135 static TypeMap CreateTypeMap (Type type, DataContractAttribute dca)
137 if (dca != null && dca.Name != null && IsInvalidNCName (dca.Name))
138 throw new InvalidDataContractException (String.Format ("DataContractAttribute for type '{0}' has an invalid name", type));
140 List<TypeMapMember> members = new List<TypeMapMember> ();
142 foreach (FieldInfo fi in type.GetFields (binding_flags)) {
143 if (fi.GetCustomAttributes (typeof (CompilerGeneratedAttribute), false).Length > 0)
146 object [] atts = fi.GetCustomAttributes (typeof (DataMemberAttribute), true);
147 if (atts.Length == 0)
149 DataMemberAttribute dma = (DataMemberAttribute) atts [0];
150 members.Add (new TypeMapField (fi, dma));
152 if (fi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length > 0)
154 members.Add (new TypeMapField (fi, null));
159 foreach (PropertyInfo pi in type.GetProperties (binding_flags)) {
160 object [] atts = pi.GetCustomAttributes (typeof (DataMemberAttribute), true);
161 if (atts.Length == 0)
163 if (pi.GetIndexParameters ().Length > 0)
165 if (IsEnumerable (pi.PropertyType) || IsDictionary (pi.PropertyType)) {
167 throw new InvalidDataContractException (String.Format ("Property {0} must have a getter", pi));
169 else if (!pi.CanRead || !pi.CanWrite)
170 throw new InvalidDataContractException (String.Format ("Non-collection property {0} must have both getter and setter", pi));
171 DataMemberAttribute dma = (DataMemberAttribute) atts [0];
172 members.Add (new TypeMapProperty (pi, dma));
176 members.Sort (delegate (TypeMapMember m1, TypeMapMember m2) { return m1.Order != m2.Order ? m1.Order - m2.Order : String.CompareOrdinal (m1.Name, m2.Name); });
177 return new TypeMap (type, dca == null ? null : dca.Name, members.ToArray ());
182 TypeMapMember [] members;
184 static readonly Type [] deser_methods_args = new Type [] { typeof (StreamingContext) };
185 const BindingFlags binding_flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
187 public TypeMap (Type type, string element, TypeMapMember [] orderedMembers)
190 this.element = element;
191 this.members = orderedMembers;
193 foreach (var mi in type.GetMethods (binding_flags)) {
194 if (mi.GetCustomAttributes (typeof (OnDeserializingAttribute), false).Length > 0)
195 OnDeserializing = mi;
196 else if (mi.GetCustomAttributes (typeof (OnDeserializedAttribute), false).Length > 0)
198 else if (mi.GetCustomAttributes (typeof (OnSerializingAttribute), false).Length > 0)
200 else if (mi.GetCustomAttributes (typeof (OnSerializedAttribute), false).Length > 0)
205 public MethodInfo OnDeserializing { get; set; }
206 public MethodInfo OnDeserialized { get; set; }
207 public MethodInfo OnSerializing { get; set; }
208 public MethodInfo OnSerialized { get; set; }
210 public virtual void Serialize (JsonSerializationWriter outputter, object graph, string type)
212 if (OnSerializing != null)
213 OnSerializing.Invoke (graph, new object [] {new StreamingContext (StreamingContextStates.All)});
215 outputter.Writer.WriteAttributeString ("type", type);
216 foreach (TypeMapMember member in members) {
217 object memberObj = member.GetMemberOf (graph);
218 // FIXME: consider EmitDefaultValue
219 outputter.Writer.WriteStartElement (member.Name);
220 outputter.WriteObjectContent (memberObj, false, false);
221 outputter.Writer.WriteEndElement ();
224 if (OnSerialized != null)
225 OnSerialized.Invoke (graph, new object [] {new StreamingContext (StreamingContextStates.All)});
228 internal static object CreateInstance (Type type)
230 if (TypeMap.IsDictionary (type)) {
231 if (type.IsGenericType)
232 return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (type.GetGenericArguments ()));
234 return new Hashtable ();
235 } else if (TypeMap.IsEnumerable (type)) {
236 if (type.IsGenericType)
237 return Activator.CreateInstance (typeof (List<>).MakeGenericType (type.GetGenericArguments ()));
239 return new ArrayList ();
242 return FormatterServices.GetUninitializedObject (type);
245 public virtual object Deserialize (JsonSerializationReader jsr, object o)
247 XmlReader reader = jsr.Reader;
248 bool isNull = reader.GetAttribute ("type") == "null";
250 object ret = isNull ? null : CreateInstance (type);
251 if (ret != null && OnDeserializing != null)
252 OnDeserializing.Invoke (ret, new object [] {new StreamingContext (StreamingContextStates.All)});
253 Dictionary<TypeMapMember,bool> filled = new Dictionary<TypeMapMember,bool> ();
255 reader.ReadStartElement ();
256 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
257 bool consumed = false;
258 for (int i = 0; i < members.Length; i++) {
259 TypeMapMember mm = members [i];
260 if (mm.Name == reader.LocalName && reader.NamespaceURI == String.Empty) {
261 if (filled.ContainsKey (mm))
262 throw new SerializationException (String.Format ("Object content '{0}' for '{1}' already appeared in the reader", reader.LocalName, type));
263 mm.SetMemberValue (ret, jsr);
272 reader.ReadEndElement ();
273 if (ret != null && OnDeserialized != null)
274 OnDeserialized.Invoke (ret, new object [] {new StreamingContext (StreamingContextStates.All)});
279 abstract class TypeMapMember
282 DataMemberAttribute dma;
284 protected TypeMapMember (MemberInfo mi, DataMemberAttribute dma)
291 get { return dma == null ? mi.Name : dma.Name ?? mi.Name; }
294 public bool EmitDefaultValue {
295 get { return dma != null && dma.EmitDefaultValue; }
298 public bool IsRequired {
299 get { return dma != null && dma.IsRequired; }
303 get { return dma != null ? dma.Order : -1; }
306 public abstract Type Type { get; }
308 public abstract object GetMemberOf (object owner);
310 public abstract void SetMemberValue (object owner, JsonSerializationReader value);
313 class TypeMapField : TypeMapMember
317 public TypeMapField (FieldInfo fi, DataMemberAttribute dma)
323 public override Type Type {
324 get { return field.FieldType; }
327 public override object GetMemberOf (object owner)
329 return field.GetValue (owner);
332 public override void SetMemberValue (object owner, JsonSerializationReader jsr)
334 field.SetValue (owner, jsr.ReadObject (this.Type));
338 class TypeMapProperty : TypeMapMember
340 PropertyInfo property;
342 public TypeMapProperty (PropertyInfo pi, DataMemberAttribute dma)
348 public override Type Type {
349 get { return property.PropertyType; }
352 public override object GetMemberOf (object owner)
354 return property.GetValue (owner, null);
357 public override void SetMemberValue (object owner, JsonSerializationReader jsr)
359 var pSetter = this.property.GetSetMethod (true);
360 if (pSetter != null) {
361 property.SetValue (owner, jsr.ReadObject (this.Type), null);
363 } else { // no setter
364 var oldValue = property.GetValue (owner, null);
366 jsr.ReadObject (this.Type, oldValue);
367 } catch (MissingMethodException e) {
368 throw new InvalidDataContractException (string.Format ("No set method for property '{0}' "
369 + "in type '{1}'.", this.property.Name, this.property.PropertyType.FullName), e);