2 // CollectionSerialization
5 // Martin Baulig (martin.baulig@xamarin.com)
7 // Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Reflection;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
37 using System.Runtime.Serialization;
38 using System.ServiceModel;
39 using NUnit.Framework;
40 using NUnit.Framework.Constraints;
42 using NUnit.Framework.SyntaxHelpers;
45 namespace MonoTests.System.Runtime.Serialization
48 public class CollectionSerialization
57 class MyList<T> : List<T>, IMyList<T>
61 interface IMyList<T> : IList
66 class CustomList<T> : IList<T>
70 public CustomList (IList<T> elements)
72 list = new List<T> ();
74 list.AddRange (elements);
77 #region IList implementation
78 public int IndexOf (T item)
80 return list.IndexOf (item);
82 public void Insert (int index, T item)
84 list.Insert (index, item);
86 public void RemoveAt (int index)
88 list.RemoveAt (index);
90 public T this [int index] {
99 #region ICollection implementation
100 public void Add (T item)
108 public bool Contains (T item)
110 return list.Contains (item);
112 public void CopyTo (T[] array, int arrayIndex)
114 list.CopyTo (array, arrayIndex);
116 public bool Remove (T item)
118 return list.Remove (item);
121 #region IEnumerable implementation
122 public IEnumerator<T> GetEnumerator ()
124 return list.GetEnumerator ();
127 #region IEnumerable implementation
128 IEnumerator IEnumerable.GetEnumerator ()
130 return GetEnumerator ();
133 #region ICollection<T> implementation
134 int ICollection<T>.Count {
139 bool ICollection<T>.IsReadOnly {
141 return ((ICollection<T>)list).IsReadOnly;
146 public override int GetHashCode ()
148 return list.GetHashCode ();
151 public override bool Equals (object obj)
153 var custom = obj as CustomList<T>;
157 if (list.Count != custom.list.Count)
160 for (int i = 0; i < list.Count; i++)
161 if (!list [i].Equals (custom.list [i]))
168 class CustomCollection<T> : CustomList<T>
170 public CustomCollection ()
175 public CustomCollection (IList<T> elements)
181 static object Serialize<T> (object arg)
183 using (var ms = new MemoryStream ()) {
185 var serializer = new DataContractSerializer (typeof(T));
186 serializer.WriteObject (ms, arg);
187 } catch (Exception ex) {
191 return new UTF8Encoding ().GetString (ms.GetBuffer (), 0, (int)ms.Position);
195 static T Deserialize<T> (string text)
197 var buffer = new UTF8Encoding ().GetBytes (text);
198 using (var ms = new MemoryStream (buffer)) {
199 var serializer = new DataContractSerializer (typeof(T));
200 return (T)serializer.ReadObject (ms);
205 public void CollectionInterfaceContract ()
207 var array = new object[3] { 1, 2, 3 };
208 var arrayResult = (string)Serialize<object[]> (array);
210 var list = new List<int> (new[] { 1, 2, 3 });
212 Assert.That (Serialize<IList> (array), Is.EqualTo (arrayResult), "#1");
213 Assert.That (Serialize<IList> (list), Is.EqualTo (arrayResult), "#2");
214 Assert.That (Serialize<IEnumerable> (list), Is.EqualTo (arrayResult), "#3");
215 Assert.That (Serialize<ICollection> (list), Is.EqualTo (arrayResult), "#4");
217 var alist = new ArrayList ();
218 alist.AddRange (array);
220 Assert.That (Serialize<IList> (alist), Is.EqualTo (arrayResult), "#5");
222 Assert.That (Deserialize<IList> (arrayResult), Is.EqualTo (list), "#6");
223 Assert.That (Deserialize<IEnumerable> (arrayResult), Is.EqualTo (list), "#7");
224 Assert.That (Deserialize<ICollection> (arrayResult), Is.EqualTo (list), "#8");
228 public void GenericCollectionInterfaceContract ()
230 var array = new[] { 1, 2, 3 };
231 var arrayResult = (string)Serialize<int[]> (array);
233 var list = new List<int> (array);
234 var mylist = new MyList<int> ();
235 mylist.AddRange (array);
237 var custom = new CustomList<int> (array);
239 Assert.That (Serialize<IList<int>> (list), Is.EqualTo (arrayResult), "#1");
240 Assert.That (Serialize<IEnumerable<int>> (list), Is.EqualTo (arrayResult), "#2");
241 Assert.That (Serialize<ICollection<int>> (list), Is.EqualTo (arrayResult), "#3");
243 Assert.That (Serialize<IList<object>> (list),
244 InstanceOf (typeof (InvalidCastException)), "#4");
246 Assert.That (Serialize<IList<int>> (mylist), Is.EqualTo (arrayResult), "#5");
247 Assert.That (Serialize<IList<int>> (list.AsReadOnly ()), Is.EqualTo (arrayResult), "#6");
248 Assert.That (Serialize<IList<int>> (custom), Is.EqualTo (arrayResult), "#7");
250 Assert.That (Deserialize<IList<int>> (arrayResult), Is.EqualTo (list), "#8");
251 Assert.That (Deserialize<List<int>> (arrayResult), Is.EqualTo (list), "#9");
255 public void CustomCollectionInterfaceContract ()
257 var array = new[] { 1, 2, 3 };
258 var arrayResult = Serialize<int[]> (array);
260 var mylist = new MyList<int> ();
261 mylist.AddRange (array);
263 Assert.That (Serialize<IList<int>> (mylist), Is.EqualTo (arrayResult), "#1");
264 Assert.That (Serialize<List<int>> (mylist), Is.EqualTo (arrayResult), "#2");
265 Assert.That (Serialize<IMyList<int>> (mylist),
266 InstanceOf (typeof (SerializationException)), "#3");
267 Assert.That (Serialize<MyList<int>> (mylist), Is.EqualTo (arrayResult), "#4");
271 public void CustomCollectionTypeContract ()
273 var array = new[] { 1, 2, 3 };
274 var arrayResult = (string)Serialize<int[]> (array);
276 var custom = new CustomList<int> (array);
278 var result = (string)Serialize<CustomList<int>> (custom);
279 Assert.That (result.Contains ("CustomListOfint"), Is.True, "#1");
280 Assert.That (Deserialize<CustomList<int>> (result), Is.EqualTo (custom), "#2");
282 var ro = array.ToList ().AsReadOnly ();
283 var result2 = (string)Serialize<ReadOnlyCollection<int>> (ro);
284 Assert.That (result2.Contains ("ReadOnlyCollectionOfint"), Is.True, "#3");
285 Assert.That (Deserialize<ReadOnlyCollection<int>> (result2), Is.EqualTo (ro), "#4");
288 * CustomList<T> implements one of the collection interfaces, but does not have
289 * a public parameterless constructor. It is therefor treated like a normal
290 * [Serializable] type and can not be deserialized from an array.
292 * The same also applies to ReadOnlyCollection<T>.
297 Deserialize<CustomList<int>> (arrayResult);
299 } catch (Exception ex) {
300 Assert.That (ex, InstanceOf (typeof (SerializationException)), "#6");
304 Deserialize<ReadOnlyCollection<int>> (arrayResult);
306 } catch (Exception ex) {
307 Assert.That (ex, InstanceOf (typeof (SerializationException)), "#8");
311 * CustomCollection<T> does have the required public parameterless constructor,
312 * so it is treated as custom collection type and serialized as array.
316 var collection = new CustomCollection<int> (array);
317 var result3 = (string)Serialize<CustomCollection<int>> (collection);
318 Assert.That (result3, Is.EqualTo (arrayResult), "#9");
319 Assert.That (Deserialize<CustomCollection<int>> (result3), Is.EqualTo (collection), "#10");
323 public void ArrayContract ()
325 var array = new[] { 1, 2, 3 };
326 var list = new List<int> (array);
328 Assert.That (Serialize<int[]> (list),
329 InstanceOf (typeof (InvalidCastException)), "#1");
330 Assert.That (Serialize<object[]> (array),
331 InstanceOf (typeof (InvalidCastException)), "#2");
335 public void ListOfArrays ()
337 var water = new[] { "Fish", "Mermaid" };
338 var land = new[] { "Horse", "Human", "Lion" };
339 var air = new[] { "Bird", "Drake" };
340 var species = new[] { water, land, air };
341 var serialized = (string)Serialize<string[][]> (species);
343 var list = new List<string[]> (species);
344 Assert.That (Serialize<IList<string[]>> (species), Is.EqualTo (serialized), "#1");
345 Assert.That (Serialize<IList<string[]>> (list), Is.EqualTo (serialized), "#2");
348 [CollectionDataContract (Name = "MyCollection")]
349 class MissingAddMethod<T> : IEnumerable<T>
351 #region IEnumerable implementation
352 public IEnumerator<T> GetEnumerator ()
354 throw new InvalidOperationException ();
357 #region IEnumerable implementation
358 IEnumerator IEnumerable.GetEnumerator ()
360 throw new InvalidOperationException ();
365 [CollectionDataContract (Name = "MyCollection")]
366 class MissingEnumerable<T>
368 public void Add (T item)
370 throw new NotImplementedException ();
374 [CollectionDataContract (Name = "MyCollection")]
375 class MyDataContractCollection<T> : IEnumerable<T>
379 public MyDataContractCollection ()
381 list = new List<T> ();
384 public MyDataContractCollection (IList<T> elements)
386 list = new List<T> ();
387 list.AddRange (elements);
390 #region IEnumerable implementation
391 public IEnumerator<T> GetEnumerator ()
393 return list.GetEnumerator ();
396 #region IEnumerable implementation
397 IEnumerator IEnumerable.GetEnumerator ()
399 return GetEnumerator ();
403 public void Add (T item)
409 class MyDerivedDataContract<T> : MyDataContractCollection<T>
414 public void TestCollectionDataContract ()
416 Assert.That (Serialize<MissingAddMethod<int>> (new MissingAddMethod<int> ()),
417 InstanceOf (typeof (InvalidDataContractException)), "#1");
418 Assert.That (Serialize<MissingEnumerable<int>> (new MissingEnumerable<int> ()),
419 InstanceOf (typeof (InvalidDataContractException)), "#2");
421 var array = new[] { 1, 2, 3 };
422 var arrayResult = (string)Serialize<int[]> (array);
423 var collection = new MyDataContractCollection<int> (array);
425 var result = Serialize<MyDataContractCollection<int>> (collection);
426 Assert.That (result, InstanceOf (typeof(string)), "#3");
428 Assert.That (Serialize<MyDataContractCollection<int>> (array),
429 InstanceOf (typeof (SerializationException)), "#4");
431 var derived = new MyDerivedDataContract<int> ();
432 Assert.That (Serialize<MyDataContractCollection<int>> (derived),
433 InstanceOf (typeof (SerializationException)), "#5");
436 Deserialize<MyDataContractCollection<int>> (arrayResult);
438 } catch (Exception ex) {
439 Assert.That (ex, InstanceOf (typeof(SerializationException)), "#7");
442 var deserialized = Deserialize<MyDataContractCollection<int>> ((string)result);
443 Assert.That (deserialized, InstanceOf (typeof (MyDataContractCollection<int>)), "#8");
449 var derived = new MyDerivedDataContract<int> ();
450 Assert.That (Serialize<MyDataContractCollection<int>> (derived),
451 InstanceOf (typeof (SerializationException)), "#5");
454 public static InstanceOfTypeConstraint InstanceOf (Type expectedType)
456 return new InstanceOfTypeConstraint (expectedType);