浏览代码

2008-11-23 Atsushi Enomoto <[email protected]>

	* SerializationMap.cs, XmlFormatterSerializer.cs,
	  XmlFormatterDeserializer.cs, DataContractAttribute.cs,
	  CollectionDataContractAttribute.cs, KnownTypeCollection.cs :
	  support IsReference (new in 3.5 SP1).

	* XmlObjectSerializerTest.cs : added test for IsReference = true.


svn path=/trunk/mcs/; revision=119734
Atsushi Eno 17 年之前
父节点
当前提交
b97d68da36

+ 7 - 0
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/ChangeLog

@@ -1,3 +1,10 @@
+2008-11-23  Atsushi Enomoto  <[email protected]>
+
+	* SerializationMap.cs, XmlFormatterSerializer.cs,
+	  XmlFormatterDeserializer.cs, DataContractAttribute.cs,
+	  CollectionDataContractAttribute.cs, KnownTypeCollection.cs :
+	  support IsReference (new in 3.5 SP1).
+
 2008-11-23  Atsushi Enomoto  <[email protected]>
 
 	* DataContractSerializer.cs : remove extra type validity check.

+ 3 - 0
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/CollectionDataContractAttribute.cs

@@ -35,6 +35,7 @@ namespace System.Runtime.Serialization
 	public sealed class CollectionDataContractAttribute : Attribute
 	{
 		string name, ns, item_name, key_name, value_name;
+		bool is_reference;
 
 		public CollectionDataContractAttribute ()
 		{
@@ -64,6 +65,8 @@ namespace System.Runtime.Serialization
 			get { return value_name; }
 			set { value_name = value; }
 		}
+
+		public bool IsReference { get; set; } // new in 3.5 SP1
 	}
 }
 #endif

+ 4 - 0
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/DataContractAttribute.cs

@@ -51,6 +51,10 @@ namespace System.Runtime.Serialization
 			get { return ns; }
 			set { ns = value; }
 		}
+
+		// new in 3.5 SP1
+		[MonoTODO]
+		public bool IsReference { get; set; }
 	}
 }
 #endif

+ 0 - 1
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/KnownTypeCollection.cs

@@ -449,7 +449,6 @@ namespace System.Runtime.Serialization
 			string ns = ((DataContractAttribute) atts [0]).Namespace;
 			if (ns == null)
 				ns = XmlObjectSerializer.DefaultNamespaceBase + type.Namespace;
-
 			return new QName (name, ns);
 		}
 

+ 35 - 2
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/SerializationMap.cs

@@ -86,6 +86,7 @@ namespace System.Runtime.Serialization
 		public readonly KnownTypeCollection KnownTypes;
 		public readonly Type RuntimeType;
 		public readonly QName XmlName;
+		public bool IsReference; // new in 3.5 SP1
 		public List<DataMemberInfo> Members;
 #if !NET_2_1
 		XmlSchemaSet schema_set;
@@ -282,6 +283,31 @@ namespace System.Runtime.Serialization
 
 		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);
+			}
+			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);
+
+			if (label != null)
+				serializer.Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
+
+			SerializeNonReference (graph, serializer);
+
+			serializer.SerializingObjects.Remove (graph);
+		}
+
+		public virtual void SerializeNonReference (object graph,
+			XmlFormatterSerializer serializer)
 		{
 			foreach (DataMemberInfo dmi in Members) {
 				FieldInfo fi = dmi.Member as FieldInfo;
@@ -385,7 +411,10 @@ namespace System.Runtime.Serialization
 		{
 			Type baseType = type;
 			List <DataMemberInfo> members = new List <DataMemberInfo> ();
-			
+			object [] atts = type.GetCustomAttributes (
+				typeof (DataContractAttribute), false);
+			IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
+
 			while (baseType != null) {
 				QName bqname = knownTypes.GetQName (baseType);
 					
@@ -454,9 +483,13 @@ namespace System.Runtime.Serialization
 		{
 			element_type = elementType;
 			element_qname = KnownTypes.GetQName (element_type);
+
+			object [] atts = type.GetCustomAttributes (
+				typeof (CollectionDataContractAttribute), false);
+			IsReference = atts.Length > 0 ? (((CollectionDataContractAttribute) atts [0]).IsReference) : false;
 		}
 
-		public override void Serialize (object graph,
+		public override void SerializeNonReference (object graph,
 			XmlFormatterSerializer serializer)
 		{
 			string ns = element_qname.Namespace;

+ 38 - 2
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs

@@ -42,6 +42,11 @@ namespace System.Runtime.Serialization
 	{
 		KnownTypeCollection types;
 		IDataContractSurrogate surrogate;
+		// 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
+		// before any references to it.
+		Hashtable references = new Hashtable ();
 
 		public static object Deserialize (XmlReader reader, Type type,
 			KnownTypeCollection knownTypes, IDataContractSurrogate surrogate,
@@ -84,9 +89,24 @@ namespace System.Runtime.Serialization
 			this.surrogate = surrogate;
 		}
 
+		public Hashtable References {
+			get { return references; }
+		}
+
 		// At the beginning phase, we still have to instantiate a new
 		// target object even if fromContent is true.
 		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);
@@ -98,17 +118,33 @@ namespace System.Runtime.Serialization
 					graph_qname = new QName (itype, reader.NamespaceURI);
 			}
 
+			string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
+			if (label != null) {
+Console.WriteLine ("Found reference: " + label);
+				object o = references [label];
+				if (o == null)
+					throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
+				reader.Skip ();
+				return o;
+			}
+
 			bool isNil = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace) == "true";
-			reader.ReadStartElement ();
-			if (isNil)
+
+			if (isNil) {
+				reader.Skip ();
 				if (!type.IsValueType)
 					return null;
 				else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
 					return null;
 				else 
 					throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
+			}
+
+			reader.ReadStartElement ();
 
 			object res = DeserializeContent (graph_qname, type, reader);
+
+			reader.MoveToContent ();
 			if (reader.NodeType == XmlNodeType.EndElement)
 				reader.ReadEndElement ();
 			else if (reader.NodeType != XmlNodeType.None)

+ 9 - 6
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterSerializer.cs

@@ -50,6 +50,7 @@ namespace System.Runtime.Serialization
 		int max_items;
 
 		ArrayList objects = new ArrayList ();
+		Hashtable references = new Hashtable (); // preserve possibly referenced objects to ids. (new in 3.5 SP1)
 
 		public static void Serialize (XmlDictionaryWriter writer, object graph,
 			KnownTypeCollection types,
@@ -69,6 +70,14 @@ namespace System.Runtime.Serialization
 			max_items = maxItems;
 		}
 
+		public ArrayList SerializingObjects {
+			get { return objects; }
+		}
+
+		public IDictionary References {
+			get { return references; }
+		}
+
 		public XmlDictionaryWriter Writer {
 			get { return writer; }
 		}
@@ -116,10 +125,6 @@ namespace System.Runtime.Serialization
 
 		public void SerializeNonPrimitive (Type type, object graph)
 		{
-			if (objects.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 ()));
-			objects.Add (graph);
-
 			Type actualType = graph.GetType ();
 
 			SerializationMap map = types.FindUserMap (actualType);
@@ -129,12 +134,10 @@ namespace System.Runtime.Serialization
 				map = types.FindUserMap (actualType);
 //				throw new InvalidDataContractException (String.Format ("Type {0} has neither Serializable nor DataContract attributes.", type));
 			}
-
 			if (type != actualType)
 				Write_i_type (map.XmlName);
 
 			map.Serialize (graph, this);
-			objects.Remove (graph);
 		}
 	}
 }

+ 4 - 0
mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/ChangeLog

@@ -1,3 +1,7 @@
+2008-11-23  Atsushi Enomoto  <[email protected]>
+
+	* XmlObjectSerializerTest.cs : added test for IsReference = true.
+
 2008-04-17  Eyal Alaluf <[email protected]>
 
 	* XmlObjectSerializerTest.cs: Add test for base class with a different XML

+ 40 - 0
mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/XmlObjectSerializerTest.cs

@@ -1020,6 +1020,30 @@ namespace MonoTests.System.Runtime.Serialization
 				.ReadObject (XmlReader.Create (new StringReader (xml)));
 		}
 
+		[Test]
+		public void ReferenceSerialization ()
+		{
+			var dc = new DataContractSerializer (typeof (ReferenceWrapper));
+			var t = new ReferenceType ();
+			StringWriter sw = new StringWriter ();
+			using (var xw = XmlWriter.Create (sw)) {
+				xw.WriteStartElement ("z", "root", "http://schemas.microsoft.com/2003/10/Serialization/");
+				dc.WriteObject (xw, new ReferenceWrapper () {T = t, T2 = t});
+				xw.WriteEndElement ();
+			}
+			string xml = @"<?xml version='1.0' encoding='utf-16'?><z:root xmlns:z='http://schemas.microsoft.com/2003/10/Serialization/'><ReferenceWrapper xmlns:i='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://schemas.datacontract.org/2004/07/MonoTests.System.Runtime.Serialization'><T z:Id='i1'><F>x</F></T><T2 z:Ref='i1' /></ReferenceWrapper></z:root>";
+			Assert.AreEqual (xml.Replace ('\'', '"'), sw.ToString (), "#1");
+
+			ReferenceWrapper w;
+			using (XmlReader r = XmlReader.Create (new StringReader (xml)))
+	{
+				r.ReadStartElement ();
+				w = (ReferenceWrapper) dc.ReadObject (r);
+				r.ReadEndElement ();
+			}
+			Assert.AreEqual (w.T, w.T2, "#2");
+		}
+
 		private T Deserialize<T> (string xml)
 		{
 			return Deserialize<T> (xml, typeof (T));
@@ -1251,6 +1275,22 @@ namespace MonoTests.System.Runtime.Serialization
 
 	}
 
+	[DataContract]
+	public class ReferenceWrapper
+	{
+	        [DataMember (Order = 1)]
+	        public ReferenceType T;
+
+	        [DataMember (Order = 2)]
+	        public ReferenceType T2;
+	}
+
+	[DataContract (IsReference = true)]
+	public class ReferenceType
+	{
+		[DataMember]
+		public string F = "x";
+	}
 }
 
 [DataContract]