exists (and raises InvalidOperationException if required).
*/
- internal abstract class SerializationMap
+ internal abstract partial class SerializationMap
{
public const BindingFlags AllInstanceFlags =
BindingFlags.Public | BindingFlags.NonPublic |
{
KnownTypes = knownTypes;
RuntimeType = type;
- if (qname.Namespace == String.Empty)
+ if (qname.Namespace == null)
qname = new QName (qname.Name,
- "http://schemas.datacontract.org/2004/07/" + type.Namespace);
+ KnownTypeCollection.DefaultClrNamespaceBase + type.Namespace);
XmlName = qname;
Members = new List<DataMemberInfo> ();
+
+ foreach (var mi in type.GetMethods (AllInstanceFlags)) {
+ if (mi.GetCustomAttributes (typeof (OnDeserializingAttribute), false).Length > 0)
+ OnDeserializing = mi;
+ else if (mi.GetCustomAttributes (typeof (OnDeserializedAttribute), false).Length > 0)
+ OnDeserialized = mi;
+ }
}
+ public MethodInfo OnDeserializing { get; set; }
+ public MethodInfo OnDeserialized { get; set; }
+
public virtual bool OutputXsiType {
get { return true; }
}
public virtual void Serialize (object graph,
XmlFormatterSerializer serializer)
{
- string label = null;
- if (IsReference) {
- label = (string) serializer.References [graph];
- if (label != null) {
- serializer.Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
- return;
- }
- label = "i" + (serializer.References.Count + 1);
- serializer.References.Add (graph, label);
- }
+ string label;
+ if (serializer.TrySerializeAsReference (IsReference, graph, out label))
+ return;
else if (serializer.SerializingObjects.Contains (graph))
throw new SerializationException (String.Format ("Circular reference of an object in the object graph was found: '{0}' of type {1}", graph, graph.GetType ()));
serializer.SerializingObjects.Add (graph);
serializer.WriteEndElement ();
}
}
-
- /* Deserialize non-primitive types */
+
+ public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
+ {
+ bool isEmpty = reader.IsEmptyElement;
+ reader.ReadStartElement ();
+ reader.MoveToContent ();
+
+ object res;
+
+ if (isEmpty)
+ res = DeserializeEmptyContent (reader, deserializer);
+ else
+ res = DeserializeContent (reader, deserializer);
+
+ reader.MoveToContent ();
+ if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
+ reader.ReadEndElement ();
+ else if (!isEmpty && reader.NodeType != XmlNodeType.None) {
+ var li = reader as IXmlLineInfo;
+ throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.{4}",
+ reader.NodeType,
+ reader.Name,
+ reader.NamespaceURI,
+ RuntimeType.FullName,
+ li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : String.Empty));
+ }
+ return res;
+ }
// This is sort of hack. The argument reader already moved ahead of
// the actual empty element.It's just for historical consistency.
public virtual object DeserializeEmptyContent (XmlReader reader,
XmlFormatterDeserializer deserializer)
{
- return DeserializeContent (reader, deserializer);
+ return DeserializeContent (reader, deserializer, true);
}
public virtual object DeserializeContent (XmlReader reader,
XmlFormatterDeserializer deserializer)
+ {
+ return DeserializeContent (reader, deserializer, false);
+ }
+
+ object DeserializeContent (XmlReader reader,
+ XmlFormatterDeserializer deserializer, bool empty)
{
object instance = FormatterServices.GetUninitializedObject (RuntimeType);
+
+ if (OnDeserializing != null)
+ OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
+
int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
bool [] filled = new bool [Members.Count];
- int memberInd = -1;
- while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
+ int memberInd = -1, ordered = -1;
+ while (!empty && reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
DataMemberInfo dmi = null;
int i = 0;
for (; i < Members.Count; i++) { // unordered
if (Members [i].Order >= 0)
break;
if (reader.LocalName == Members [i].XmlName &&
- reader.NamespaceURI == Members [i].XmlRootNamespace) {
+ (Members [i].XmlRootNamespace == null || reader.NamespaceURI == Members [i].XmlRootNamespace)) {
memberInd = i;
dmi = Members [i];
break;
}
}
- for (; i < Members.Count; i++) { // ordered
+ for (i = Math.Max (i, ordered); i < Members.Count; i++) { // ordered
if (dmi != null)
break;
if (reader.LocalName == Members [i].XmlName &&
- reader.NamespaceURI == Members [i].XmlRootNamespace) {
+ (Members [i].XmlRootNamespace == null || reader.NamespaceURI == Members [i].XmlRootNamespace)) {
memberInd = i;
+ ordered = i;
dmi = Members [i];
break;
}
}
SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
filled [memberInd] = true;
+ reader.MoveToContent ();
}
for (int i = 0; i < Members.Count; i++)
if (!filled [i] && Members [i].IsRequired)
throw MissingRequiredMember (Members [i], reader);
+ if (OnDeserialized != null)
+ OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
+
return instance;
}
// For now it could be private.
protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
{
- return new ArgumentException (String.Format ("Data contract member {0} is required, but missing in the input XML.", new QName (dmi.XmlName, dmi.XmlNamespace)));
+ var li = reader as IXmlLineInfo;
+ return new ArgumentException (String.Format ("Data contract member {0} for the type {1} is required, but missing in the input XML.{2}",
+ new QName (dmi.XmlName, dmi.XmlNamespace),
+ RuntimeType,
+ li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : null));
}
// For now it could be private.
protected void SetValue (DataMemberInfo dmi, object obj, object value)
{
- if (dmi.Member is PropertyInfo)
- ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
- else
- ((FieldInfo) dmi.Member).SetValue (obj, value);
+ try {
+ if (dmi.Member is PropertyInfo)
+ ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
+ else
+ ((FieldInfo) dmi.Member).SetValue (obj, value);
+ } catch (Exception ex) {
+ throw new InvalidOperationException (String.Format ("Failed to set value of type {0} for property {1}", value != null ? value.GetType () : null, dmi.Member), ex);
+ }
}
protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
}
}
- internal class XmlSerializableMap : SerializationMap
+ internal partial class XmlSerializableMap : SerializationMap
{
public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
: base (type, qname, knownTypes)
ixs.WriteXml (serializer.Writer);
}
+ public override object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
+ {
+#if NET_2_1
+ IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType);
+#else
+ IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType, true);
+#endif
+ ixs.ReadXml (reader);
+ return ixs;
+ }
+
#if !NET_2_1
// FIXME: verify return value sanity.
public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
#endif
}
- internal class SharedContractMap : SerializationMap
+ internal partial class SharedContractMap : SerializationMap
{
public SharedContractMap (
Type type, QName qname, KnownTypeCollection knownTypes)
: base (type, qname, knownTypes)
{
- Type baseType = type;
+ }
+
+ internal void Initialize ()
+ {
+ Type baseType = RuntimeType;
List <DataMemberInfo> members = new List <DataMemberInfo> ();
- object [] atts = type.GetCustomAttributes (
+ object [] atts = baseType.GetCustomAttributes (
typeof (DataContractAttribute), false);
IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
while (baseType != null) {
- QName bqname = knownTypes.GetQName (baseType);
+ QName bqname = KnownTypes.GetQName (baseType);
members = GetMembers (baseType, bqname, true);
+ members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
Members.InsertRange (0, members);
members.Clear ();
baseType = baseType.BaseType;
}
-
-// Members.Sort (delegate (
-// DataMemberInfo d1, DataMemberInfo d2) {
-// return d1.Order - d2.Order;
-// });
}
List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
GetDataMemberAttribute (fi);
if (dma == null)
continue;
- if (fi.IsInitOnly)
- throw new InvalidDataContractException (String.Format (
- "DataMember field {0} must not be read-only.", fi));
data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
}
- data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
-
return data_members;
}
public override List<DataMemberInfo> GetMembers ()
{
- return GetMembers (RuntimeType, XmlName, true);
+ return Members;
}
}
- internal class DefaultTypeMap : SerializationMap
+ internal partial class DefaultTypeMap : SerializationMap
{
public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
- : base (type, KnownTypeCollection.GetContractQName (type, null, null), knownTypes)
+ : base (type, KnownTypeCollection.GetStaticQName (type), knownTypes)
{
Members.AddRange (GetDefaultMembers ());
}
mt = pi.PropertyType;
if (mt == null)
continue;
+ if (mi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length != 0)
+ continue;
l.Add (new DataMemberInfo (mi, new DataMemberAttribute (), null, null));
}
+ l.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
return l;
}
}
// FIXME: it still needs to consider ItemName/KeyName/ValueName
// (especially Dictionary collection is not likely considered yet.)
- internal class CollectionContractTypeMap : CollectionTypeMap
+ internal partial class CollectionContractTypeMap : CollectionTypeMap
{
+ CollectionDataContractAttribute a;
+
public CollectionContractTypeMap (
Type type, CollectionDataContractAttribute a, Type elementType,
QName qname, KnownTypeCollection knownTypes)
: base (type, elementType, qname, knownTypes)
{
+ this.a = a;
IsReference = a.IsReference;
}
{
}
- internal class CollectionTypeMap : SerializationMap, ICollectionTypeMap
+ internal partial class CollectionTypeMap : SerializationMap, ICollectionTypeMap
{
Type element_type;
internal QName element_qname;
{
element_type = elementType;
element_qname = KnownTypes.GetQName (element_type);
- var icoll = RuntimeType.GetInterfaces ().FirstOrDefault (
- iface => iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>));
+ var icoll = GetGenericCollectionInterface (RuntimeType);
if (icoll != null) {
if (RuntimeType.IsInterface) {
add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
}
}
+ static Type GetGenericCollectionInterface (Type type)
+ {
+ foreach (var iface in type.GetInterfaces ())
+ if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>))
+ return iface;
+
+ return null;
+ }
+
public override bool OutputXsiType {
get { return false; }
}
{
if (RuntimeType.IsArray)
return new ArrayList ();
- else
+ if (RuntimeType.IsInterface) {
+ var icoll = GetGenericCollectionInterface (RuntimeType);
+ if (icoll != null)
+ return Activator.CreateInstance (typeof (List<>).MakeGenericType (RuntimeType.GetGenericArguments () [0])); // List<T>
+ else // non-generic
+ return new ArrayList ();
+ }
#if NET_2_1 // FIXME: is it fine?
- return Activator.CreateInstance (RuntimeType);
+ return Activator.CreateInstance (RuntimeType);
#else
- return Activator.CreateInstance (RuntimeType, true);
+ return Activator.CreateInstance (RuntimeType, true);
#endif
}
public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
{
- return CreateInstance ();
+ var instance = CreateInstance ();
+ if (OnDeserializing != null)
+ OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
+ try {
+ if (RuntimeType.IsArray)
+ return ((ArrayList)instance).ToArray (element_type);
+ else
+ return instance;
+ } finally {
+ if (OnDeserialized != null)
+ OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
+ }
}
public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
{
object instance = CreateInstance ();
+ if (OnDeserializing != null)
+ OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
object elem = deserializer.Deserialize (element_type, reader);
add_method.Invoke (instance, new object [] {elem});
else
throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
+ reader.MoveToContent ();
+ }
+ try {
+ if (RuntimeType.IsArray)
+ return ((ArrayList)instance).ToArray (element_type);
+ return instance;
+ } finally {
+ if (OnDeserialized != null)
+ OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
}
- if (RuntimeType.IsArray)
- return ((ArrayList)instance).ToArray (element_type);
- return instance;
}
public override List<DataMemberInfo> GetMembers ()
#endif
}
- internal class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
+ internal partial class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
{
Type key_type, value_type;
- QName dict_qname, item_qname, key_qname, value_qname;
+ QName item_qname, key_qname, value_qname;
MethodInfo add_method;
CollectionDataContractAttribute a;
key_type = typeof (object);
value_type = typeof (object);
- var idic = RuntimeType.GetInterfaces ().FirstOrDefault (
- iface => iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>));
+ var idic = GetGenericDictionaryInterface (RuntimeType);
if (idic != null) {
var imap = RuntimeType.GetInterfaceMap (idic);
for (int i = 0; i < imap.InterfaceMethods.Length; i++)
value_qname = GetValueQName ();
}
+ static Type GetGenericDictionaryInterface (Type type)
+ {
+ foreach (var iface in type.GetInterfaces ())
+ if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>))
+ return iface;
+
+ return null;
+ }
+
string ContractNamespace {
get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
}
public Type KeyType { get { return key_type; } }
public Type ValueType { get { return value_type; } }
- static readonly QName kvpair_key_qname = new QName ("Key", KnownTypeCollection.MSArraysNamespace);
- static readonly QName kvpair_value_qname = new QName ("Value", KnownTypeCollection.MSArraysNamespace);
-
internal virtual QName GetDictionaryQName ()
{
- if (a != null && !String.IsNullOrEmpty (a.Name))
- return new QName (a.Name, ContractNamespace);
- return new QName ("ArrayOf" + GetItemQName ().Name, KnownTypeCollection.MSArraysNamespace);
+ string name = a != null ? a.Name : null;
+ string ns = a != null ? a.Namespace : null;
+ if (RuntimeType.IsGenericType && RuntimeType.GetGenericTypeDefinition () != typeof (Dictionary<,>))
+ name = name ?? KnownTypeCollection.GetDefaultName (RuntimeType);
+ else
+ name = "ArrayOf" + GetItemQName ().Name;
+ ns = ns ?? KnownTypeCollection.MSArraysNamespace;
+
+ return new QName (name, ns);
}
internal virtual QName GetItemQName ()
{
- if (a != null && !String.IsNullOrEmpty (a.ItemName))
- return new QName (a.ItemName, ContractNamespace);
- return new QName ("KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name, KnownTypeCollection.MSArraysNamespace);
+ string name = a != null ? a.ItemName : null;
+ string ns = a != null ? a.Namespace : null;
+
+ name = name ?? "KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name;
+ ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
+
+ return new QName (name, ns);
}
internal virtual QName GetKeyQName ()
{
- if (a != null && !String.IsNullOrEmpty (a.KeyName))
- return new QName (a.KeyName, ContractNamespace);
- return kvpair_key_qname;
+ string name = a != null ? a.KeyName : null;
+ string ns = a != null ? a.Namespace : null;
+
+ name = name ?? "Key";
+ ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
+ return new QName (name, ns);
}
internal virtual QName GetValueQName ()
{
- if (a != null && !String.IsNullOrEmpty (a.ValueName))
- return new QName (a.ValueName, ContractNamespace);
- return kvpair_value_qname;
+ string name = a != null ? a.ValueName : null;
+ string ns = a != null ? a.Namespace : null;
+
+ name = name ?? "Value";
+ ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
+ return new QName (name, ns);
}
internal virtual string CurrentNamespace {
}
}
- public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
+ object CreateInstance ()
{
+ if (RuntimeType.IsInterface) {
+ if (RuntimeType.IsGenericType && Array.IndexOf (RuntimeType.GetGenericTypeDefinition ().GetInterfaces (), typeof (IDictionary<,>)) >= 0) {
+ var gargs = RuntimeType.GetGenericArguments ();
+ return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (gargs [0], gargs [1])); // Dictionary<T>
+ }
+ else // non-generic
+ return new Hashtable ();
+ }
#if NET_2_1 // FIXME: is it fine?
- object instance = Activator.CreateInstance (RuntimeType);
+ return Activator.CreateInstance (RuntimeType);
#else
- object instance = Activator.CreateInstance (RuntimeType, true);
+ return Activator.CreateInstance (RuntimeType, true);
#endif
+ }
+
+ public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+ {
+ return DeserializeContent (reader, deserializer);
+ }
+
+ public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
+ {
+ object instance = CreateInstance ();
int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
if (reader.IsEmptyElement)
#endif
}
- internal class SharedTypeMap : SerializationMap
+ internal partial class SharedTypeMap : SerializationMap
{
public SharedTypeMap (
Type type, QName qname, KnownTypeCollection knownTypes)
: base (type, qname, knownTypes)
{
- Members = GetMembers (type, XmlName, false);
+ }
+
+ public void Initialize ()
+ {
+ Members = GetMembers (RuntimeType, XmlName, false);
}
List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
{
List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
- int order = 0;
BindingFlags flags = AllInstanceFlags;
if (declared_only)
flags |= BindingFlags.DeclaredOnly;
if (fi.IsInitOnly)
throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
DataMemberAttribute dma = new DataMemberAttribute ();
- dma.Order = order++;
data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
}
+ data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance); // alphabetic order.
+
return data_members;
}
// Does this make sense? I doubt.
public override List<DataMemberInfo> GetMembers ()
{
- return GetMembers (RuntimeType, XmlName, true);
+ return Members;
+ //return GetMembers (RuntimeType, XmlName, true);
}
}
- internal class EnumMap : SerializationMap
+ internal partial class EnumMap : SerializationMap
{
List<EnumMemberInfo> enum_members;
+ bool flag_attr;
public EnumMap (
Type type, QName qname, KnownTypeCollection knownTypes)
typeof (DataContractAttribute), false);
if (atts.Length != 0)
has_dc = true;
+ flag_attr = type.GetCustomAttributes (typeof (FlagsAttribute), false).Length > 0;
enum_members = new List<EnumMemberInfo> ();
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
"Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
}
+ public override object DeserializeEmptyContent (XmlReader reader,
+ XmlFormatterDeserializer deserializer)
+ {
+ if (!flag_attr)
+ throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
+ return Enum.ToObject (RuntimeType, 0);
+ }
+
public override object DeserializeContent (XmlReader reader,
XmlFormatterDeserializer deserializer)
{
return emi.Value;
}
- throw new SerializationException (String.Format (
- "Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
+ if (!flag_attr)
+ throw new SerializationException (String.Format ("Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
+ return Enum.ToObject (RuntimeType, 0);
}
}
public int Compare (DataMemberInfo d1, DataMemberInfo d2)
{
- if (d1.Order == -1 || d2.Order == -1)
+ if (d1.Order == d2.Order)
return String.CompareOrdinal (d1.XmlName, d2.XmlName);
return d1.Order - d2.Order;