Add DataContractResolver stuff (dummy), and fix Id reference from inside the referenc...
authorAtsushi Eno <atsushi@ximian.com>
Thu, 30 Sep 2010 11:56:00 +0000 (20:56 +0900)
committerAtsushi Eno <atsushi@ximian.com>
Thu, 30 Sep 2010 11:56:00 +0000 (20:56 +0900)
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/DataContractResolver.cs [new file with mode: 0644]
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/DataContractSerializer.cs
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/SerializationMap.cs
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs
mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/XmlObjectSerializerTest.cs
mcs/class/System.Runtime.Serialization/moonlight_raw_System.Runtime.Serialization.dll.sources
mcs/class/System.ServiceModel/System.ServiceModel.Description/DataContractSerializerOperationBehavior.cs

index bd7f739a23cd97b8b7cafccf28dfce5a820e105e..6fe2ca3c8a5a1fed9aa0cdb5f30618e30fd1647e 100644 (file)
@@ -13,6 +13,7 @@ System.Runtime.Serialization.Configuration/TypeElementCollection.cs
 System.Runtime.Serialization/CollectionDataContractAttribute.cs
 System.Runtime.Serialization/ContractNamespaceAttribute.cs
 System.Runtime.Serialization/DataContractAttribute.cs
+System.Runtime.Serialization/DataContractResolver.cs
 System.Runtime.Serialization/DataContractSerializer.cs
 System.Runtime.Serialization/DataMemberAttribute.cs
 System.Runtime.Serialization/EnumMemberAttribute.cs
diff --git a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/DataContractResolver.cs b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/DataContractResolver.cs
new file mode 100644 (file)
index 0000000..eac98ed
--- /dev/null
@@ -0,0 +1,47 @@
+//
+// DataContractResolver.cs
+//
+// Author:
+//     Atsushi Enomoto <atsushi@ximian.com>
+//
+// Copyright (C) 2010 Novell, Inc.  http://www.novell.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Xml;
+
+namespace System.Runtime.Serialization
+{
+       // See http://msdn.microsoft.com/en-us/library/ee358759.aspx
+#if NET_4_0
+       [MonoTODO ("not in use yet")]
+       public
+#else
+       internal
+#endif
+       abstract class DataContractResolver
+       {
+               public abstract Type ResolveName (string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver);
+
+               public abstract bool TryResolveType (Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace);
+       }
+}
index b0adbce1e8438a324759674875e9fc40fdfbe3e6..6693daa0e7260fdb3e84a718a80546996e86f3e4 100755 (executable)
@@ -171,6 +171,48 @@ namespace System.Runtime.Serialization
                                dataContractSurrogate);
                }
 
+#if NET_4_0
+               public DataContractSerializer (Type type,
+                       IEnumerable<Type> knownTypes,
+                       int maxObjectsInGraph,
+                       bool ignoreExtensionDataObject,
+                       bool preserveObjectReferences,
+                       IDataContractSurrogate dataContractSurrogate,
+                       DataContractResolver dataContractResolver)
+                       : this (type, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
+               {
+                       DataContractResolver = dataContractResolver;
+               }
+
+               public DataContractSerializer (Type type,
+                       string rootName,
+                       string rootNamespace,
+                       IEnumerable<Type> knownTypes,
+                       int maxObjectsInGraph,
+                       bool ignoreExtensionDataObject,
+                       bool preserveObjectReferences,
+                       IDataContractSurrogate dataContractSurrogate,
+                       DataContractResolver dataContractResolver)
+                       : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
+               {
+                       DataContractResolver = dataContractResolver;
+               }
+
+               public DataContractSerializer (Type type,
+                       XmlDictionaryString rootName,
+                       XmlDictionaryString rootNamespace,
+                       IEnumerable<Type> knownTypes,
+                       int maxObjectsInGraph,
+                       bool ignoreExtensionDataObject,
+                       bool preserveObjectReferences,
+                       IDataContractSurrogate dataContractSurrogate,
+                       DataContractResolver dataContractResolver)
+                       : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
+               {
+                       DataContractResolver = dataContractResolver;
+               }
+#endif
+
                void PopulateTypes (IEnumerable<Type> knownTypes)
                {
                        if (known_types == null)
@@ -217,6 +259,13 @@ namespace System.Runtime.Serialization
                        PopulateTypes (Type.EmptyTypes);
                }
 
+#if NET_4_0
+               public
+#else
+               internal
+#endif
+               DataContractResolver DataContractResolver { get; private set; }
+
                public bool IgnoreExtensionDataObject {
                        get { return ignore_ext; }
                }
@@ -266,7 +315,6 @@ namespace System.Runtime.Serialization
                        return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
                }
 
-               [MonoTODO]
                public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
                {
                        int startTypeCount = known_types.Count;
@@ -275,7 +323,7 @@ namespace System.Runtime.Serialization
                        bool isEmpty = reader.IsEmptyElement;
 
                        object ret = XmlFormatterDeserializer.Deserialize (reader, type,
-                               known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName);
+                               known_types, surrogate, DataContractResolver, root_name.Value, root_ns.Value, verifyObjectName);
 
                        // remove temporarily-added known types for
                        // rootType and object graph type.
@@ -285,6 +333,19 @@ namespace System.Runtime.Serialization
                        return ret;
                }
 
+#if NET_4_0
+               public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
+               {
+                       var bak = DataContractResolver;
+                       try {
+                               DataContractResolver = resolver;
+                               return ReadObject (reader, verifyObjectName);
+                       } finally {
+                               DataContractResolver = bak;
+                       }
+               }
+#endif
+
                private void ReadRootStartElement (XmlReader reader, Type type)
                {
                        SerializationMap map =
@@ -304,6 +365,19 @@ namespace System.Runtime.Serialization
                        WriteObject (w, graph);
                }
 
+#if NET_4_0
+               public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
+               {
+                       var bak = DataContractResolver;
+                       try {
+                               DataContractResolver = resolver;
+                               WriteObject (writer, graph);
+                       } finally {
+                               DataContractResolver = bak;
+                       }
+               }
+#endif
+
                [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
                /*
                        when writeContentOnly is true, then the input XmlWriter
index 8dc987d1783e6cb6a61f5d60f2cd428c186fc955..5a74cc479e19f5dfc359962c886cf244ef60a805 100644 (file)
@@ -124,6 +124,17 @@ namespace System.Runtime.Serialization
 
                public QName XmlName { get; set; }
 
+               protected void HandleId (XmlReader reader, XmlFormatterDeserializer deserializer, object instance)
+               {
+                       HandleId (reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace), deserializer, instance);
+               }
+               
+               protected void HandleId (string id, XmlFormatterDeserializer deserializer, object instance)
+               {
+                       if (id != null)
+                               deserializer.References.Add (id, instance);
+               }
+
                public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
                {
                        object [] atts = type.GetCustomAttributes (
@@ -343,15 +354,16 @@ namespace System.Runtime.Serialization
                public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
                {
                        bool isEmpty = reader.IsEmptyElement;
+                       string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
                        reader.ReadStartElement ();
                        reader.MoveToContent ();
 
                        object res;
 
                        if (isEmpty)
-                               res = DeserializeEmptyContent (reader, deserializer);
+                               res = DeserializeEmptyContent (reader, deserializer, id);
                        else
-                               res = DeserializeContent (reader, deserializer);
+                               res = DeserializeContent (reader, deserializer, id);
 
                        reader.MoveToContent ();
                        if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
@@ -371,21 +383,21 @@ namespace System.Runtime.Serialization
                // 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)
+                       XmlFormatterDeserializer deserializer, string id)
                {
-                       return DeserializeContent (reader, deserializer, true);
+                       return DeserializeContent (reader, deserializer, id, true);
                }
 
                public virtual object DeserializeContent (XmlReader reader,
-                       XmlFormatterDeserializer deserializer)
+                       XmlFormatterDeserializer deserializer, string id)
                {
-                       return DeserializeContent (reader, deserializer, false);
+                       return DeserializeContent (reader, deserializer, id, false);
                }
 
-               object DeserializeContent (XmlReader reader,
-                       XmlFormatterDeserializer deserializer, bool empty)
+               object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id, bool empty)
                {
                        object instance = FormatterServices.GetUninitializedObject (RuntimeType);
+                       HandleId (id, deserializer, instance);
 
                        if (OnDeserializing != null)
                                OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
@@ -495,6 +507,9 @@ namespace System.Runtime.Serialization
 #else
                        IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType, true);
 #endif
+
+                       HandleId (reader, deserializer, ixs);
+
                        ixs.ReadXml (reader);
                        return ixs;
                }
@@ -709,9 +724,10 @@ namespace System.Runtime.Serialization
 #endif
                }
 
-               public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+               public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
                {
                        var instance = CreateInstance ();
+                       HandleId (id, deserializer, instance);
                        if (OnDeserializing != null)
                                OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
                        try {
@@ -725,9 +741,10 @@ namespace System.Runtime.Serialization
                        }
                }
 
-               public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+               public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
                {
                        object instance = CreateInstance ();
+                       HandleId (id, deserializer, instance);
                        if (OnDeserializing != null)
                                OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
                        int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
@@ -961,14 +978,15 @@ namespace System.Runtime.Serialization
 #endif
                }
 
-               public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+               public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
                {
-                       return DeserializeContent (reader, deserializer);
+                       return DeserializeContent (reader, deserializer, id);
                }
 
-               public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
+               public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer, string id)
                {
                        object instance = CreateInstance ();
+                       HandleId (id, deserializer, instance);
                        int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
                        while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
                                if (reader.IsEmptyElement)
@@ -1141,18 +1159,19 @@ namespace System.Runtime.Serialization
                                "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
                }
 
-               public override object DeserializeEmptyContent (XmlReader reader,
-                       XmlFormatterDeserializer deserializer)
+               public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
                {
                        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);
+                       var instance = Enum.ToObject (RuntimeType, 0);
+                       HandleId (id, deserializer, instance);
+                       return instance;
                }
 
-               public override object DeserializeContent (XmlReader reader,
-                       XmlFormatterDeserializer deserializer)
+               public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
                {
                        string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
+                       HandleId (id, deserializer, value);
 
                        if (value != String.Empty) {
                                foreach (EnumMemberInfo emi in enum_members)
index 105ff7b1ffd189ab76fe3467f825dc04e0d32072..3491c2f0ef78ab539082fe7e05a40730626ba04d 100644 (file)
@@ -43,6 +43,7 @@ namespace System.Runtime.Serialization
        {
                KnownTypeCollection types;
                IDataContractSurrogate surrogate;
+               DataContractResolver resolver; // new in 4.0.
                // 3.5 SP1 supports deserialization by reference (id->obj).
                // Though unlike XmlSerializer, it does not support forward-
                // reference resolution i.e. a referenced object must appear
@@ -50,7 +51,7 @@ namespace System.Runtime.Serialization
                Hashtable references = new Hashtable ();
 
                public static object Deserialize (XmlReader reader, Type type,
-                       KnownTypeCollection knownTypes, IDataContractSurrogate surrogate,
+                       KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, DataContractResolver resolver,
                        string name, string ns, bool verifyObjectName)
                {
                        reader.MoveToContent ();
@@ -60,7 +61,7 @@ namespace System.Runtime.Serialization
                                    reader.NamespaceURI != ns)
                                        throw new SerializationException (String.Format ("Expected element '{0}' in namespace '{1}', but found {2} node '{3}' in namespace '{4}'", name, ns, reader.NodeType, reader.LocalName, reader.NamespaceURI));
 //                             Verify (knownTypes, type, name, ns, reader);
-                       return new XmlFormatterDeserializer (knownTypes, surrogate).Deserialize (type, reader);
+                       return new XmlFormatterDeserializer (knownTypes, surrogate, resolver).Deserialize (type, reader);
                }
 
                // Verify the top element name and namespace.
@@ -88,30 +89,20 @@ namespace System.Runtime.Serialization
 
                private XmlFormatterDeserializer (
                        KnownTypeCollection knownTypes,
-                       IDataContractSurrogate surrogate)
+                       IDataContractSurrogate surrogate,
+                       DataContractResolver resolver)
                {
                        this.types = knownTypes;
                        this.surrogate = surrogate;
+                       this.resolver = resolver;
                }
 
                public Hashtable References {
                        get { return references; }
                }
 
-               // At the beginning phase, we still have to instantiate a new
-               // target object even if fromContent is true.
+               // This method handles z:Ref, xsi:nil and primitive types, and then delegates to DeserializeByMap() for anything else.
                public object Deserialize (Type type, XmlReader reader)
-               {
-                       string label = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
-                       object o = DeserializeCore (type, reader);
-
-                       if (label != null)
-                               references.Add (label, o);
-
-                       return o;
-               }
-
-               public object DeserializeCore (Type type, XmlReader reader)
                {
                        QName graph_qname = types.GetQName (type);
                        string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
@@ -145,6 +136,8 @@ namespace System.Runtime.Serialization
                        }
 
                        if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname.Name) != null) {
+                               string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
+
                                string value;
                                if (reader.IsEmptyElement) {
                                        reader.Read (); // advance
@@ -156,7 +149,13 @@ namespace System.Runtime.Serialization
                                }
                                else
                                        value = reader.ReadElementContentAsString ();
-                               return KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
+                               object ret = KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
+                               if (id != null) {
+                                       if (references.ContainsKey (id))
+                                               throw new InvalidOperationException (String.Format ("Object with Id '{0}' already exists as '{1}'", id, references [id]));
+                                       references.Add (id, ret);
+                               }
+                               return ret;
                        }
 
                        return DeserializeByMap (graph_qname, type, reader);
index 97b30da8b1e7020b75165f0a24ad852cf1c920f2..30e878330c838c205e02a364d13a78f4a763abbc 100755 (executable)
@@ -1428,6 +1428,27 @@ namespace MonoTests.System.Runtime.Serialization
                        Assert.IsTrue (idx2 < 0, "idx2 should not occur at " + idx2);
                }
 
+               [Test]
+               public void AncestralReference ()
+               {
+                       // Reference to Parent comes inside the Parent itself.
+                       // In this case, adding reference after complete deserialization won't work (but it should).
+                       var ms = new MemoryStream ();
+                       Type [] knownTypes = new Type [] { typeof (ParentClass), typeof (Foo), typeof (Bar) };
+
+                       var ds = new DataContractSerializer (typeof (Parent));
+
+                       var org = new Parent ();
+                       ds.WriteObject (ms, org);
+                       string result = Encoding.UTF8.GetString (ms.ToArray ());
+                       ms.Position = 0;
+
+                       var parent = (Parent) ds.ReadObject (ms);
+
+                       Assert.IsNotNull (parent.Child, "#1");
+                       Assert.AreEqual (parent, parent.Child.Parent, "#2");
+               }
+
                [Test]
                public void IXmlSerializableCallConstructor ()
                {
@@ -1954,4 +1975,34 @@ public class Dict<T> : Dictionary<string, T> where T : ParentClass
        }
        
 }
+
+[DataContract (IsReference = true)]
+public class Parent
+{
+       //constructor
+       public Parent ()
+       {
+               Child = new Child (this);
+       }
+
+       [DataMember]
+       public Child Child;
+}
+
+[DataContract]
+public class Child
+{
+       public Child ()
+       {
+       }
+       
+       public Child (Parent parent)
+       {
+               this.Parent = parent;
+       }
+       
+       [DataMember]
+       public Parent Parent;
+}
+
 #endregion
index 59cd659ac4d1d9b74ed4106078f893e7fc2df7e3..537804ecd8536fd4caed0032c589aa9e61309c2b 100644 (file)
@@ -2,6 +2,7 @@ Assembly/AssemblyInfo.cs
 System.Runtime.Serialization/CollectionDataContractAttribute.cs
 System.Runtime.Serialization/ContractNamespaceAttribute.cs
 System.Runtime.Serialization/DataContractAttribute.cs
+System.Runtime.Serialization/DataContractResolver.cs
 System.Runtime.Serialization/DataContractSerializer.cs
 System.Runtime.Serialization/DataMemberAttribute.cs
 System.Runtime.Serialization/EnumMemberAttribute.cs
index 58f421e75f1b436ade03daa406a7f3c56d4ae787..1e3b92f5f2ce5c748b8aad4c5e15985f5890e4cb 100644 (file)
@@ -59,6 +59,10 @@ namespace System.ServiceModel.Description
                        get { return format; }
                }
 
+#if NET_4_0
+               public DataContractResolver DataContractResolver { get; set; }
+#endif
+
                public bool IgnoreExtensionDataObject { get; set; }
 
                public int MaxItemsInObjectGraph { get; set; }