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.Serialization;
40 namespace System.Runtime.Serialization.Json
44 static bool IsInvalidNCName (string name)
46 if (name == null || name.Length == 0)
49 XmlConvert.VerifyNCName (name);
50 } catch (XmlException) {
56 public static TypeMap CreateTypeMap (Type type)
58 object [] atts = type.GetCustomAttributes (typeof (DataContractAttribute), true);
60 return CreateTypeMap (type, (DataContractAttribute) atts [0]);
62 atts = type.GetCustomAttributes (typeof (SerializableAttribute), false);
64 return CreateTypeMap (type, null);
66 if (IsPrimitiveType (type))
69 return CreateDefaultTypeMap (type);
72 static bool IsPrimitiveType (Type type)
76 if (Type.GetTypeCode (type) != TypeCode.Object)
77 return true; // FIXME: it is likely hacky
81 static TypeMap CreateDefaultTypeMap (Type type)
83 var l = new List<TypeMapMember> ();
84 foreach (var fi in type.GetFields ())
85 l.Add (new TypeMapField (fi, null));
86 foreach (var pi in type.GetProperties ())
87 if (pi.CanRead && pi.CanWrite)
88 l.Add (new TypeMapProperty (pi, null));
89 l.Sort ((x, y) => x.Order != y.Order ? x.Order - y.Order : String.Compare (x.Name, y.Name, StringComparison.Ordinal));
90 return new TypeMap (type, null, l.ToArray ());
93 static bool IsCollection (Type type)
95 if (type.GetInterface ("System.Collections.IList", false) != null)
97 if (type.GetInterface ("System.Collections.Generic.IList`1", false) != null)
99 if (type.GetInterface ("System.Collections.Generic.ICollection`1", false) != null)
104 static TypeMap CreateTypeMap (Type type, DataContractAttribute dca)
106 if (dca != null && dca.Name != null && IsInvalidNCName (dca.Name))
107 throw new InvalidDataContractException (String.Format ("DataContractAttribute for type '{0}' has an invalid name", type));
109 List<TypeMapMember> members = new List<TypeMapMember> ();
111 foreach (FieldInfo fi in type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
113 object [] atts = fi.GetCustomAttributes (typeof (DataMemberAttribute), true);
114 if (atts.Length == 0)
116 DataMemberAttribute dma = (DataMemberAttribute) atts [0];
117 members.Add (new TypeMapField (fi, dma));
119 if (fi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length > 0)
121 members.Add (new TypeMapField (fi, null));
126 foreach (PropertyInfo pi in type.GetProperties (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
127 object [] atts = pi.GetCustomAttributes (typeof (DataMemberAttribute), true);
128 if (atts.Length == 0)
130 if (pi.GetIndexParameters ().Length > 0)
132 if (IsCollection (pi.PropertyType)) {
134 throw new InvalidDataContractException (String.Format ("Property {0} must have a getter", pi));
136 else if (!pi.CanRead || !pi.CanWrite)
137 throw new InvalidDataContractException (String.Format ("Non-collection property {0} must have both getter and setter", pi));
138 DataMemberAttribute dma = (DataMemberAttribute) atts [0];
139 members.Add (new TypeMapProperty (pi, dma));
143 members.Sort (delegate (TypeMapMember m1, TypeMapMember m2) { return m1.Order != m2.Order ? m1.Order - m2.Order : String.CompareOrdinal (m1.Name, m2.Name); });
144 return new TypeMap (type, dca == null ? null : dca.Name, members.ToArray ());
149 TypeMapMember [] members;
151 public TypeMap (Type type, string element, TypeMapMember [] orderedMembers)
154 this.element = element;
155 this.members = orderedMembers;
158 public void Serialize (JsonSerializationWriter outputter, object graph)
160 foreach (TypeMapMember member in members) {
161 object memberObj = member.GetMemberOf (graph);
162 // FIXME: consider EmitDefaultValue
163 outputter.Writer.WriteStartElement (member.Name);
164 outputter.WriteObjectContent (memberObj, false, false);
165 outputter.Writer.WriteEndElement ();
169 public object Deserialize (JsonSerializationReader jsr)
171 XmlReader reader = jsr.Reader;
172 object ret = FormatterServices.GetUninitializedObject (type);
173 Dictionary<TypeMapMember,bool> filled = new Dictionary<TypeMapMember,bool> ();
175 reader.ReadStartElement ();
176 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
177 bool consumed = false;
178 for (int i = 0; i < members.Length; i++) {
179 TypeMapMember mm = members [i];
180 if (mm.Name == reader.LocalName && reader.NamespaceURI == String.Empty) {
181 if (filled.ContainsKey (mm))
182 throw new SerializationException (String.Format ("Object content '{0}' for '{1}' already appeared in the reader", reader.LocalName, type));
183 mm.SetMemberValue (ret, jsr.ReadObject (mm.Type));
192 reader.ReadEndElement ();
197 abstract class TypeMapMember
200 DataMemberAttribute dma;
202 protected TypeMapMember (MemberInfo mi, DataMemberAttribute dma)
209 get { return dma == null ? mi.Name : dma.Name ?? mi.Name; }
212 public bool EmitDefaultValue {
213 get { return dma != null && dma.EmitDefaultValue; }
216 public bool IsRequired {
217 get { return dma != null && dma.IsRequired; }
221 get { return dma != null ? dma.Order : -1; }
224 public abstract Type Type { get; }
226 public abstract object GetMemberOf (object owner);
228 public abstract void SetMemberValue (object owner, object value);
231 class TypeMapField : TypeMapMember
235 public TypeMapField (FieldInfo fi, DataMemberAttribute dma)
241 public override Type Type {
242 get { return field.FieldType; }
245 public override object GetMemberOf (object owner)
247 return field.GetValue (owner);
250 public override void SetMemberValue (object owner, object value)
252 field.SetValue (owner, value);
256 class TypeMapProperty : TypeMapMember
258 PropertyInfo property;
260 public TypeMapProperty (PropertyInfo pi, DataMemberAttribute dma)
266 public override Type Type {
267 get { return property.PropertyType; }
270 public override object GetMemberOf (object owner)
272 return property.GetValue (owner, null);
275 public override void SetMemberValue (object owner, object value)
277 property.SetValue (owner, value, null);