// // CollectionSerialization // // Authors: // Martin Baulig (martin.baulig@xamarin.com) // // Copyright 2012 Xamarin Inc. (http://www.xamarin.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.IO; using System.Linq; using System.Text; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.Serialization; using System.ServiceModel; using NUnit.Framework; using NUnit.Framework.Constraints; #if !MOBILE using NUnit.Framework.SyntaxHelpers; #endif namespace MonoTests.System.Runtime.Serialization { [TestFixture] public class CollectionSerialization { [DataContract] class Foo { [DataMember] public int Hello; } class MyList : List, IMyList { } interface IMyList : IList { } [Serializable] class CustomList : IList { List list; public CustomList (IList elements) { list = new List (); if (elements != null) list.AddRange (elements); } #region IList implementation public int IndexOf (T item) { return list.IndexOf (item); } public void Insert (int index, T item) { list.Insert (index, item); } public void RemoveAt (int index) { list.RemoveAt (index); } public T this [int index] { get { return list [index]; } set { list [index] = value; } } #endregion #region ICollection implementation public void Add (T item) { list.Add (item); } public void Clear () { list.Clear (); } public bool Contains (T item) { return list.Contains (item); } public void CopyTo (T[] array, int arrayIndex) { list.CopyTo (array, arrayIndex); } public bool Remove (T item) { return list.Remove (item); } #endregion #region IEnumerable implementation public IEnumerator GetEnumerator () { return list.GetEnumerator (); } #endregion #region IEnumerable implementation IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } #endregion #region ICollection implementation int ICollection.Count { get { return list.Count; } } bool ICollection.IsReadOnly { get { return ((ICollection)list).IsReadOnly; } } #endregion public override int GetHashCode () { return list.GetHashCode (); } public override bool Equals (object obj) { var custom = obj as CustomList; if (custom == null) return false; if (list.Count != custom.list.Count) return false; for (int i = 0; i < list.Count; i++) if (!list [i].Equals (custom.list [i])) return false; return true; } } class CustomCollection : CustomList { public CustomCollection () : base (null) { } public CustomCollection (IList elements) : base (elements) { } } static object Serialize (object arg) { using (var ms = new MemoryStream ()) { try { var serializer = new DataContractSerializer (typeof(T)); serializer.WriteObject (ms, arg); } catch (Exception ex) { return ex; } return new UTF8Encoding ().GetString (ms.GetBuffer (), 0, (int)ms.Position); } } static T Deserialize (string text) { var buffer = new UTF8Encoding ().GetBytes (text); using (var ms = new MemoryStream (buffer)) { var serializer = new DataContractSerializer (typeof(T)); return (T)serializer.ReadObject (ms); } } [Test] public void CollectionInterfaceContract () { var array = new object[3] { 1, 2, 3 }; var arrayResult = (string)Serialize (array); var list = new List (new[] { 1, 2, 3 }); Assert.That (Serialize (array), Is.EqualTo (arrayResult), "#1"); Assert.That (Serialize (list), Is.EqualTo (arrayResult), "#2"); Assert.That (Serialize (list), Is.EqualTo (arrayResult), "#3"); Assert.That (Serialize (list), Is.EqualTo (arrayResult), "#4"); var alist = new ArrayList (); alist.AddRange (array); Assert.That (Serialize (alist), Is.EqualTo (arrayResult), "#5"); Assert.That (Deserialize (arrayResult), Is.EqualTo (list), "#6"); Assert.That (Deserialize (arrayResult), Is.EqualTo (list), "#7"); Assert.That (Deserialize (arrayResult), Is.EqualTo (list), "#8"); } [Test] public void GenericCollectionInterfaceContract () { var array = new[] { 1, 2, 3 }; var arrayResult = (string)Serialize (array); var list = new List (array); var mylist = new MyList (); mylist.AddRange (array); var custom = new CustomList (array); Assert.That (Serialize> (list), Is.EqualTo (arrayResult), "#1"); Assert.That (Serialize> (list), Is.EqualTo (arrayResult), "#2"); Assert.That (Serialize> (list), Is.EqualTo (arrayResult), "#3"); Assert.That (Serialize> (list), InstanceOf (typeof (InvalidCastException)), "#4"); Assert.That (Serialize> (mylist), Is.EqualTo (arrayResult), "#5"); Assert.That (Serialize> (list.AsReadOnly ()), Is.EqualTo (arrayResult), "#6"); Assert.That (Serialize> (custom), Is.EqualTo (arrayResult), "#7"); Assert.That (Deserialize> (arrayResult), Is.EqualTo (list), "#8"); Assert.That (Deserialize> (arrayResult), Is.EqualTo (list), "#9"); } [Test] public void CustomCollectionInterfaceContract () { var array = new[] { 1, 2, 3 }; var arrayResult = Serialize (array); var mylist = new MyList (); mylist.AddRange (array); Assert.That (Serialize> (mylist), Is.EqualTo (arrayResult), "#1"); Assert.That (Serialize> (mylist), Is.EqualTo (arrayResult), "#2"); Assert.That (Serialize> (mylist), InstanceOf (typeof (SerializationException)), "#3"); Assert.That (Serialize> (mylist), Is.EqualTo (arrayResult), "#4"); } [Test] public void CustomCollectionTypeContract () { var array = new[] { 1, 2, 3 }; var arrayResult = (string)Serialize (array); var custom = new CustomList (array); var result = (string)Serialize> (custom); Assert.That (result.Contains ("CustomListOfint"), Is.True, "#1"); Assert.That (Deserialize> (result), Is.EqualTo (custom), "#2"); var ro = array.ToList ().AsReadOnly (); var result2 = (string)Serialize> (ro); Assert.That (result2.Contains ("ReadOnlyCollectionOfint"), Is.True, "#3"); Assert.That (Deserialize> (result2), Is.EqualTo (ro), "#4"); /* * CustomList implements one of the collection interfaces, but does not have * a public parameterless constructor. It is therefor treated like a normal * [Serializable] type and can not be deserialized from an array. * * The same also applies to ReadOnlyCollection. * */ try { Deserialize> (arrayResult); Assert.Fail ("#5"); } catch (Exception ex) { Assert.That (ex, InstanceOf (typeof (SerializationException)), "#6"); } try { Deserialize> (arrayResult); Assert.Fail ("#7"); } catch (Exception ex) { Assert.That (ex, InstanceOf (typeof (SerializationException)), "#8"); } /* * CustomCollection does have the required public parameterless constructor, * so it is treated as custom collection type and serialized as array. * */ var collection = new CustomCollection (array); var result3 = (string)Serialize> (collection); Assert.That (result3, Is.EqualTo (arrayResult), "#9"); Assert.That (Deserialize> (result3), Is.EqualTo (collection), "#10"); } [Test] public void ArrayContract () { var array = new[] { 1, 2, 3 }; var list = new List (array); Assert.That (Serialize (list), InstanceOf (typeof (InvalidCastException)), "#1"); Assert.That (Serialize (array), InstanceOf (typeof (InvalidCastException)), "#2"); } [Test] public void ListOfArrays () { var water = new[] { "Fish", "Mermaid" }; var land = new[] { "Horse", "Human", "Lion" }; var air = new[] { "Bird", "Drake" }; var species = new[] { water, land, air }; var serialized = (string)Serialize (species); var list = new List (species); Assert.That (Serialize> (species), Is.EqualTo (serialized), "#1"); Assert.That (Serialize> (list), Is.EqualTo (serialized), "#2"); } [CollectionDataContract (Name = "MyCollection")] class MissingAddMethod : IEnumerable { #region IEnumerable implementation public IEnumerator GetEnumerator () { throw new InvalidOperationException (); } #endregion #region IEnumerable implementation IEnumerator IEnumerable.GetEnumerator () { throw new InvalidOperationException (); } #endregion } [CollectionDataContract (Name = "MyCollection")] class MissingEnumerable { public void Add (T item) { throw new NotImplementedException (); } } [CollectionDataContract (Name = "MyCollection")] class MyDataContractCollection : IEnumerable { List list; public MyDataContractCollection () { list = new List (); } public MyDataContractCollection (IList elements) { list = new List (); list.AddRange (elements); } #region IEnumerable implementation public IEnumerator GetEnumerator () { return list.GetEnumerator (); } #endregion #region IEnumerable implementation IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } #endregion public void Add (T item) { list.Add (item); } } class MyDerivedDataContract : MyDataContractCollection { } [Test] public void TestCollectionDataContract () { Assert.That (Serialize> (new MissingAddMethod ()), InstanceOf (typeof (InvalidDataContractException)), "#1"); Assert.That (Serialize> (new MissingEnumerable ()), InstanceOf (typeof (InvalidDataContractException)), "#2"); var array = new[] { 1, 2, 3 }; var arrayResult = (string)Serialize (array); var collection = new MyDataContractCollection (array); var result = Serialize> (collection); Assert.That (result, InstanceOf (typeof(string)), "#3"); Assert.That (Serialize> (array), InstanceOf (typeof (SerializationException)), "#4"); var derived = new MyDerivedDataContract (); Assert.That (Serialize> (derived), InstanceOf (typeof (SerializationException)), "#5"); try { Deserialize> (arrayResult); Assert.Fail ("#6"); } catch (Exception ex) { Assert.That (ex, InstanceOf (typeof(SerializationException)), "#7"); } var deserialized = Deserialize> ((string)result); Assert.That (deserialized, InstanceOf (typeof (MyDataContractCollection)), "#8"); } [Test] public void Test () { var derived = new MyDerivedDataContract (); Assert.That (Serialize> (derived), InstanceOf (typeof (SerializationException)), "#5"); } public static InstanceOfTypeConstraint InstanceOf (Type expectedType) { return new InstanceOfTypeConstraint (expectedType); } } }