From: Antoine Cailliau Date: Mon, 14 Apr 2014 07:40:31 +0000 (+0200) Subject: [Fix] DataContractJsonSerializer fails serializing/deserializing collections X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=b2b269b1e8c654c7acbfb9448acc7d6dce63299a;p=mono.git [Fix] DataContractJsonSerializer fails serializing/deserializing collections --- diff --git a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs index 37a5b74981c..295161932b5 100644 --- a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs +++ b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs @@ -194,6 +194,10 @@ namespace System.Runtime.Serialization.Json return new JsonSerializationReader (this, reader, type, verifyObjectName).ReadRoot (); } catch (SerializationException) { throw; + } catch (InvalidDataContractException) { + throw; + } catch (System.Reflection.TargetInvocationException ex) { + throw ex.InnerException; } catch (Exception ex) { throw new SerializationException ("Deserialization has failed", ex); } diff --git a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs index 29e3cfa5d05..8f8fe7171fe 100644 --- a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs +++ b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs @@ -68,6 +68,11 @@ namespace System.Runtime.Serialization.Json } public object ReadObject (Type type) + { + return ReadObject (type, null); + } + + public object ReadObject (Type type, object instance) { if (serialized_object_count ++ == serializer.MaxItemsInObjectGraph) throw SerializationError (String.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer.MaxItemsInObjectGraph)); @@ -175,10 +180,10 @@ namespace System.Runtime.Serialization.Json Type ct = GetCollectionElementType (type); if (ct != null) { - return DeserializeGenericCollection (type, ct); + return DeserializeGenericCollection (type, ct, instance); } else { TypeMap map = GetTypeMap (type); - return map.Deserialize (this); + return map.Deserialize (this, instance); } } else @@ -278,24 +283,15 @@ namespace System.Runtime.Serialization.Json return idx < 0 ? s : String.Concat (s.Substring (idx + 2), ".", s.Substring (0, idx)); } - IEnumerable GetInterfaces2 (Type type) - { - if (type.IsInterface) - yield return type; - foreach (var t in type.GetInterfaces ()) - yield return t; - } - Type GetCollectionElementType (Type type) { if (type.IsArray) return type.GetElementType (); - if (type.IsGenericType) { - // returns T for IEnumerable - foreach (Type i in GetInterfaces2 (type)) - if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>))) - return i.GetGenericArguments () [0]; - } + + var inter = type.GetInterface ("System.Collections.Generic.IEnumerable`1", false); + if (inter != null) + return inter.GetGenericArguments () [0]; + if (typeof (IEnumerable).IsAssignableFrom (type)) // return typeof(object) for mere collection. return typeof (object); @@ -303,16 +299,19 @@ namespace System.Runtime.Serialization.Json return null; } - object DeserializeGenericCollection (Type collectionType, Type elementType) + object DeserializeGenericCollection (Type collectionType, Type elementType, object collectionInstance) { reader.ReadStartElement (); object ret; if (collectionType.IsInterface) collectionType = typeof (List<>).MakeGenericType (elementType); if (TypeMap.IsDictionary (collectionType)) { - object dic = Activator.CreateInstance (collectionType); - var itemSetter = dic.GetType ().GetProperty ("Item"); - var keyarr = new object [1]; + if (collectionInstance == null) + collectionInstance = Activator.CreateInstance (collectionType); + + var keyType = elementType.IsGenericType ? elementType.GetGenericArguments () [0] : typeof (object); + var valueType = elementType.IsGenericType ? elementType.GetGenericArguments () [1] : typeof (object); + MethodInfo add = collectionType.GetMethod ("Add", new Type [] { keyType, valueType }); for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) { if (!reader.IsStartElement ("item")) @@ -321,21 +320,26 @@ namespace System.Runtime.Serialization.Json // reading a KeyValuePair in the form of reader.Read (); reader.MoveToContent (); - object key = ReadObject (elementType.GetGenericArguments () [0]); + object key = ReadObject (keyType); reader.MoveToContent (); - object val = ReadObject (elementType.GetGenericArguments () [1]); + object val = ReadObject (valueType); reader.Read (); - keyarr [0] = key; - itemSetter.SetValue (dic, val, keyarr); + add.Invoke (collectionInstance, new [] { key, val }); } - ret = dic; + ret = collectionInstance; } else if (typeof (IList).IsAssignableFrom (collectionType)) { #if NET_2_1 Type listType = collectionType.IsArray ? typeof (List<>).MakeGenericType (elementType) : null; #else Type listType = collectionType.IsArray ? typeof (ArrayList) : null; #endif - IList c = (IList) Activator.CreateInstance (listType ?? collectionType); + + IList c; + if (collectionInstance == null) + c = (IList) Activator.CreateInstance (listType ?? collectionType); + else + c = (IList) collectionInstance; + for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) { if (!reader.IsStartElement ("item")) throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI)); @@ -355,21 +359,31 @@ namespace System.Runtime.Serialization.Json ret = collectionType.IsArray ? ((ArrayList) c).ToArray (elementType) : c; #endif } else { - object c = Activator.CreateInstance (collectionType); - MethodInfo add = collectionType.GetMethod ("Add", new Type [] {elementType}); + if (collectionInstance == null) + collectionInstance = Activator.CreateInstance (collectionType); + + MethodInfo add; + if (collectionInstance.GetType ().IsGenericType && + collectionInstance.GetType ().GetGenericTypeDefinition () == typeof (LinkedList<>)) + add = collectionType.GetMethod ("AddLast", new Type [] { elementType }); + else + add = collectionType.GetMethod ("Add", new Type [] { elementType }); + if (add == null) { var icoll = typeof (ICollection<>).MakeGenericType (elementType); - if (icoll.IsAssignableFrom (c.GetType ())) + if (icoll.IsAssignableFrom (collectionInstance.GetType ())) add = icoll.GetMethod ("Add"); } + if (add == null) + throw new MissingMethodException (elementType.FullName, "Add"); for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) { if (!reader.IsStartElement ("item")) throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI)); - object elem = ReadObject (elementType); - add.Invoke (c, new object [] {elem}); + object element = ReadObject (elementType); + add.Invoke (collectionInstance, new object [] { element }); } - ret = c; + ret = collectionInstance; } reader.ReadEndElement (); diff --git a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationWriter.cs b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationWriter.cs index 217b6b16c0f..9ccf447e5bf 100644 --- a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationWriter.cs +++ b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationWriter.cs @@ -137,9 +137,9 @@ namespace System.Runtime.Serialization.Json writer.WriteEndElement (); writer.WriteEndElement (); } - } else if (TypeMap.IsCollection (type)) { // array + } else if (graph is Array || TypeMap.IsEnumerable (type)) { writer.WriteAttributeString ("type", "array"); - foreach (object o in (ICollection) graph) { + foreach (object o in (IEnumerable) graph) { writer.WriteStartElement ("item"); // when it is typed, then no need to output "__type" WriteObjectContent (o, false, !(graph is Array && type.GetElementType () != typeof (object))); diff --git a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs index 3f1cbb510c9..8378a70c220 100644 --- a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs +++ b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs @@ -94,18 +94,40 @@ namespace System.Runtime.Serialization.Json internal static bool IsDictionary (Type type) { - if (type.GetInterface ("System.Collections.IDictionary", false) != null) + Type inter; + inter = type.GetInterface ("System.Collections.IDictionary", false); + if (inter != null + && type.GetMethod ("Add", new Type[] { typeof (object), typeof (object) }) != null) return true; - if (type.GetInterface ("System.Collections.Generic.IDictionary`2", false) != null) + + inter = type.GetInterface ("System.Collections.Generic.IDictionary`2", false); + if (inter != null + && type.GetMethod ("Add", new Type[] { inter.GetGenericArguments() [0], + inter.GetGenericArguments() [1] }) != null) return true; return false; } - internal static bool IsCollection (Type type) + internal static bool IsEnumerable (Type type) { + if (type.IsGenericType && + type.GetGenericTypeDefinition() == typeof (LinkedList<>)) + return true; + if (IsPrimitiveType (type) || IsDictionary (type)) return false; - if (type.GetInterface ("System.Collections.IEnumerable", false) != null) + + Type inter; + inter = type.GetInterface ("System.Collections.Generic.IReadOnlyCollection`1", false); + if (inter != null) + return true; + + inter = type.GetInterface ("System.Collections.IEnumerable", false); + if (inter != null && type.GetMethod ("Add", new Type[] { typeof (object) }) != null) + return true; + + inter = type.GetInterface ("System.Collections.Generic.IEnumerable`1", false); + if (inter != null && type.GetMethod ("Add", new Type[] { inter.GetGenericArguments() [0] }) != null) return true; return false; } @@ -140,7 +162,7 @@ namespace System.Runtime.Serialization.Json continue; if (pi.GetIndexParameters ().Length > 0) continue; - if (IsCollection (pi.PropertyType)) { + if (IsEnumerable (pi.PropertyType) || IsDictionary (pi.PropertyType)) { if (!pi.CanRead) throw new InvalidDataContractException (String.Format ("Property {0} must have a getter", pi)); } @@ -210,7 +232,7 @@ namespace System.Runtime.Serialization.Json return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (type.GetGenericArguments ())); else return new Hashtable (); - } else if (TypeMap.IsCollection (type)) { + } else if (TypeMap.IsEnumerable (type)) { if (type.IsGenericType) return Activator.CreateInstance (typeof (List<>).MakeGenericType (type.GetGenericArguments ())); else @@ -220,7 +242,7 @@ namespace System.Runtime.Serialization.Json return FormatterServices.GetUninitializedObject (type); } - public virtual object Deserialize (JsonSerializationReader jsr) + public virtual object Deserialize (JsonSerializationReader jsr, object o) { XmlReader reader = jsr.Reader; bool isNull = reader.GetAttribute ("type") == "null"; @@ -238,7 +260,7 @@ namespace System.Runtime.Serialization.Json if (mm.Name == reader.LocalName && reader.NamespaceURI == String.Empty) { if (filled.ContainsKey (mm)) throw new SerializationException (String.Format ("Object content '{0}' for '{1}' already appeared in the reader", reader.LocalName, type)); - mm.SetMemberValue (ret, jsr.ReadObject (mm.Type)); + mm.SetMemberValue (ret, jsr); filled [mm] = true; consumed = true; break; @@ -285,7 +307,7 @@ namespace System.Runtime.Serialization.Json public abstract object GetMemberOf (object owner); - public abstract void SetMemberValue (object owner, object value); + public abstract void SetMemberValue (object owner, JsonSerializationReader value); } class TypeMapField : TypeMapMember @@ -306,10 +328,10 @@ namespace System.Runtime.Serialization.Json { return field.GetValue (owner); } - - public override void SetMemberValue (object owner, object value) + + public override void SetMemberValue (object owner, JsonSerializationReader jsr) { - field.SetValue (owner, value); + field.SetValue (owner, jsr.ReadObject (this.Type)); } } @@ -332,9 +354,21 @@ namespace System.Runtime.Serialization.Json return property.GetValue (owner, null); } - public override void SetMemberValue (object owner, object value) + public override void SetMemberValue (object owner, JsonSerializationReader jsr) { - property.SetValue (owner, value, null); + var pSetter = this.property.GetSetMethod (true); + if (pSetter != null) { + property.SetValue (owner, jsr.ReadObject (this.Type), null); + + } else { // no setter + var oldValue = property.GetValue (owner, null); + try { + jsr.ReadObject (this.Type, oldValue); + } catch (MissingMethodException e) { + throw new InvalidDataContractException (string.Format ("No set method for property '{0}' " + + "in type '{1}'.", this.property.Name, this.property.PropertyType.FullName), e); + } + } } } } diff --git a/mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs b/mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs index fbe9250e572..7bcb40ccf8f 100644 --- a/mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs +++ b/mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs @@ -4,6 +4,7 @@ // Author: // Atsushi Enomoto // Ankit Jain +// Antoine Cailliau // // Copyright (C) 2005-2007 Novell, Inc. http://www.novell.com @@ -1486,6 +1487,322 @@ namespace MonoTests.System.Runtime.Serialization.Json Assert.AreEqual ("foo/bar/members", entity.MembersRelativeLink.ToString ()); } + + #region Test methods for collection serialization + + [Test] + public void TestArrayListSerialization () + { + var collection = new ArrayListContainer (); + var expectedOutput = "{\"Items\":[\"banana\",\"apple\"]}"; + var expectedItemsCount = 4; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + + stream.Position = 0; + collection = (ArrayListContainer) serializer.ReadObject (stream); + + Assert.AreEqual (expectedItemsCount, collection.Items.Count, "#2"); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestBitArraySerialization () + { + var collection = new BitArrayContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + public void TestHashtableSerialization () + { + var collection = new HashtableContainer (); + var expectedOutput = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}"; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void TestHashtableDeserialization () + { + var collection = new HashtableContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + serializer.ReadObject (stream); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestQueueSerialization () + { + var collection = new QueueContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + public void TestSortedListSerialization () + { + var collection = new SortedListContainer (); + var expectedOutput = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}"; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void TestSortedListDeserialization () + { + var collection = new SortedListContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + serializer.ReadObject (stream); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestStackSerialization () + { + var collection = new StackContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + public void TestEnumerableWithAddSerialization () + { + var collection = new EnumerableWithAddContainer (); + var expectedOutput = "{\"Items\":[\"banana\",\"apple\"]}"; + var expectedItemsCount = 4; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + + stream.Position = 0; + collection = (EnumerableWithAddContainer) serializer.ReadObject (stream); + + Assert.AreEqual (expectedItemsCount, collection.Items.Count, "#2"); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestEnumerableWithSpecialAddSerialization () + { + var collection = new EnumerableWithSpecialAddContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + public void TestHashSetSerialization () + { + var collection = new GenericHashSetContainer (); + var expectedOutput = "{\"Items\":[\"banana\",\"apple\"]}"; + var expectedItemsCount = 2; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + + stream.Position = 0; + collection = (GenericHashSetContainer) serializer.ReadObject (stream); + + Assert.AreEqual (expectedItemsCount, collection.Items.Count, "#2"); + } + + [Test] + public void TestLinkedListSerialization () + { + var collection = new GenericLinkedListContainer (); + var expectedOutput = "{\"Items\":[\"banana\",\"apple\"]}"; + var expectedItemsCount = 4; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + + stream.Position = 0; + collection = (GenericLinkedListContainer) serializer.ReadObject (stream); + + Assert.AreEqual (expectedItemsCount, collection.Items.Count, "#2"); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestGenericQueueSerialization () + { + var collection = new GenericQueueContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestGenericStackSerialization () + { + var collection = new GenericStackContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + public void TestGenericDictionarySerialization () + { + var collection = new GenericDictionaryContainer (); + var expectedOutput = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}"; + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void TestGenericDictionaryDeserialization () + { + var collection = new GenericDictionaryContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + serializer.ReadObject (stream); + } + + [Test] + public void TestGenericSortedListSerialization () + { + var collection = new GenericSortedListContainer (); + var expectedOutput = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}"; + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void TestGenericSortedListDeserialization () + { + var collection = new GenericSortedListContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + serializer.ReadObject (stream); + } + + [Test] + public void TestGenericSortedDictionarySerialization () + { + var collection = new GenericSortedDictionaryContainer (); + var expectedOutput = "{\"Items\":[{\"Key\":\"key1\",\"Value\":\"banana\"},{\"Key\":\"key2\",\"Value\":\"apple\"}]}"; + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void TestGenericSortedDictionaryDeserialization () + { + var collection = new GenericSortedDictionaryContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + serializer.ReadObject (stream); + } + + [Test] + public void TestGenericEnumerableWithAddSerialization () + { + var collection = new GenericEnumerableWithAddContainer (); + var expectedOutput = "{\"Items\":[\"banana\",\"apple\"]}"; + var expectedItemsCount = 4; + + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + + stream.Position = 0; + Assert.AreEqual (expectedOutput, new StreamReader (stream).ReadToEnd (), "#1"); + + stream.Position = 0; + collection = (GenericEnumerableWithAddContainer) serializer.ReadObject (stream); + + Assert.AreEqual (expectedItemsCount, collection.Items.Count, "#2"); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestGenericEnumerableWithSpecialAddSerialization () + { + var collection = new GenericEnumerableWithSpecialAddContainer (); + var serializer = new DataContractJsonSerializer (collection.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, collection); + } + + [Test] + [ExpectedException (typeof (InvalidDataContractException))] + public void TestNonCollectionGetOnlyProperty () + { + var o = new NonCollectionGetOnlyContainer (); + var serializer = new DataContractJsonSerializer (o.GetType ()); + var stream = new MemoryStream (); + serializer.WriteObject (stream, o); + } + + #endregion } public class CharTest @@ -1808,4 +2125,351 @@ public class Bug13485Type public string GetValue { get { return this.Value; } } } +#region Test classes for Collection serialization + +[DataContract] + public abstract class CollectionContainer + { + V items; + + [DataMember] + public V Items + { + get { + if (items == null) items = Init (); + return items; + } + } + + public CollectionContainer () + { + Init (); + } + + protected abstract V Init (); + } + + [DataContract] + public class ArrayListContainer : CollectionContainer { + protected override ArrayList Init () + { + return new ArrayList { "banana", "apple" }; + } + } + + [DataContract] + public class BitArrayContainer : CollectionContainer { + protected override BitArray Init () + { + return new BitArray (new [] { false, true }); + } + } + + [DataContract] + public class HashtableContainer : CollectionContainer { + protected override Hashtable Init () + { + var ht = new Hashtable (); + ht.Add ("key1", "banana"); + ht.Add ("key2", "apple"); + return ht; + } + } + + [DataContract] + public class QueueContainer : CollectionContainer { + protected override Queue Init () + { + var q = new Queue (); + q.Enqueue ("banana"); + q.Enqueue ("apple"); + return q; + } + } + + [DataContract] + public class SortedListContainer : CollectionContainer { + protected override SortedList Init () + { + var l = new SortedList (); + l.Add ("key1", "banana"); + l.Add ("key2", "apple"); + return l; + } + } + + [DataContract] + public class StackContainer : CollectionContainer { + protected override Stack Init () + { + var s = new Stack (); + s.Push ("banana"); + s.Push ("apple"); + return s; + } + } + + public class EnumerableWithAdd : IEnumerable + { + private ArrayList items; + + public EnumerableWithAdd() + { + items = new ArrayList(); + } + + public IEnumerator GetEnumerator() + { + return items.GetEnumerator(); + } + + public void Add(object value) + { + items.Add(value); + } + + public int Count + { + get { + return items.Count; + } + } + } + + public class EnumerableWithSpecialAdd : IEnumerable + { + private ArrayList items; + + public EnumerableWithSpecialAdd() + { + items = new ArrayList(); + } + + public IEnumerator GetEnumerator() + { + return items.GetEnumerator(); + } + + public void Add(object value, int index) + { + items.Add(value); + } + + public int Count + { + get + { + return items.Count; + } + } + } + + [DataContract] + public class EnumerableWithAddContainer : CollectionContainer + { + protected override EnumerableWithAdd Init() + { + var s = new EnumerableWithAdd(); + s.Add ("banana"); + s.Add ("apple"); + return s; + } + } + + [DataContract] + public class EnumerableWithSpecialAddContainer : CollectionContainer + { + protected override EnumerableWithSpecialAdd Init() + { + var s = new EnumerableWithSpecialAdd(); + s.Add("banana", 0); + s.Add("apple", 0); + return s; + } + } + + [DataContract] + public class GenericDictionaryContainer : CollectionContainer> { + protected override Dictionary Init () + { + var d = new Dictionary (); + d.Add ("key1", "banana"); + d.Add ("key2", "apple"); + return d; + } + } + + [DataContract] + public class GenericHashSetContainer : CollectionContainer> { + protected override HashSet Init () + { + return new HashSet { "banana", "apple" }; + } + } + + [DataContract] + public class GenericLinkedListContainer : CollectionContainer> { + protected override LinkedList Init () + { + var l = new LinkedList (); + l.AddFirst ("apple"); + l.AddFirst ("banana"); + return l; + } + } + + [DataContract] + public class GenericListContainer : CollectionContainer> { + protected override List Init () + { + return new List { "banana", "apple" }; + } + } + + [DataContract] + public class GenericQueueContainer : CollectionContainer> { + protected override Queue Init () + { + var q = new Queue (); + q.Enqueue ("banana"); + q.Enqueue ("apple" ); + return q; + } + } + + [DataContract] + public class GenericSortedDictionaryContainer : CollectionContainer> { + protected override SortedDictionary Init () + { + var d = new SortedDictionary (); + d.Add ("key1", "banana"); + d.Add ("key2", "apple"); + return d; + } + } + + [DataContract] + public class GenericSortedListContainer : CollectionContainer> { + protected override SortedList Init () + { + var d = new SortedList (); + d.Add ("key1", "banana"); + d.Add ("key2", "apple"); + return d; + } + } + + [DataContract] + public class GenericStackContainer : CollectionContainer> { + protected override Stack Init () + { + var s = new Stack (); + s.Push ("banana"); + s.Push ("apple" ); + return s; + } + } + + public class GenericEnumerableWithAdd : IEnumerable + { + private List items; + + public GenericEnumerableWithAdd() + { + items = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return items.GetEnumerator (); + } + + public IEnumerator GetEnumerator() + { + return items.GetEnumerator (); + } + + public void Add(string value) + { + items.Add(value); + } + + public int Count + { + get { + return items.Count; + } + } + } + + public class GenericEnumerableWithSpecialAdd : IEnumerable + { + private List items; + + public GenericEnumerableWithSpecialAdd() + { + items = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return items.GetEnumerator (); + } + + public IEnumerator GetEnumerator() + { + return items.GetEnumerator (); + } + + public void Add(string value, int index) + { + items.Add(value); + } + + public int Count + { + get + { + return items.Count; + } + } + } + + [DataContract] + public class GenericEnumerableWithAddContainer : CollectionContainer + { + protected override GenericEnumerableWithAdd Init() + { + var s = new GenericEnumerableWithAdd(); + s.Add ("banana"); + s.Add ("apple"); + return s; + } + } + + [DataContract] + public class GenericEnumerableWithSpecialAddContainer : CollectionContainer + { + protected override GenericEnumerableWithSpecialAdd Init() + { + var s = new GenericEnumerableWithSpecialAdd(); + s.Add("banana", 0); + s.Add("apple", 0); + return s; + } + } + + [DataContract] + public class NonCollectionGetOnlyContainer + { + string _test = "my string"; + + [DataMember] + public string MyString { + get { + return _test; + } + } + } +#endregion \ No newline at end of file