[Fix] DataContractJsonSerializer fails serializing/deserializing collections
authorAntoine Cailliau <a.cailliau@ac-graphic.net>
Mon, 14 Apr 2014 07:40:31 +0000 (09:40 +0200)
committerAntoine Cailliau <a.cailliau@ac-graphic.net>
Wed, 16 Apr 2014 13:10:17 +0000 (15:10 +0200)
mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs
mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs
mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationWriter.cs
mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs
mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs

index 37a5b74981cb83e8d978033e428d0a3115b59187..295161932b55c2bc9b4873d642d094ceef59b064 100644 (file)
@@ -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);
                        }
index 29e3cfa5d05f9e16093ba8340f140430341ef619..8f8fe7171fe51a2edc99763e6a79ba4d215e2bed 100644 (file)
@@ -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<Type> 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<T>
-                               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 <Key .../><Value .../>
                                        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 ();
index 217b6b16c0f595c1f22d4b15d1edaf8cf097bafb..9ccf447e5bf927c49f9d28a133f3208254b409e6 100644 (file)
@@ -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)));
index 3f1cbb510c987815b781679013b22c6f3b0ef360..8378a70c220dcf9f7cba75b9347ccb19a6e91d31 100644 (file)
@@ -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);
+                               }
+                       }
                }
        }
 }
index fbe9250e572d3d2cac86618e3894daf35121e77c..7bcb40ccf8f8774cd82ff7effe091a1d0e5fab39 100644 (file)
@@ -4,6 +4,7 @@
 // Author:
 //     Atsushi Enomoto <atsushi@ximian.com>
 //     Ankit Jain <JAnkit@novell.com>
+//     Antoine Cailliau <antoinecailliau@gmail.com>
 //
 // 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>
+       {
+               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<ArrayList> {
+               protected override ArrayList Init ()
+               {
+                       return new ArrayList { "banana", "apple" };
+               }
+       }
+       
+       [DataContract]
+       public class BitArrayContainer : CollectionContainer<BitArray> {
+               protected override BitArray Init ()
+               {
+                       return new BitArray (new [] { false, true });
+               }
+       }
+       
+       [DataContract]
+       public class HashtableContainer : CollectionContainer<Hashtable> {
+               protected override Hashtable Init ()
+               {
+                       var ht = new Hashtable ();
+                       ht.Add ("key1", "banana");
+                       ht.Add ("key2", "apple");
+                       return ht;
+               }
+       }
+       
+       [DataContract]
+       public class QueueContainer : CollectionContainer<Queue> {
+               protected override Queue Init ()
+               {
+                       var q = new Queue ();
+                       q.Enqueue ("banana");
+                       q.Enqueue ("apple");
+                       return q;
+               }
+       }
+       
+       [DataContract]
+       public class SortedListContainer : CollectionContainer<SortedList> {
+               protected override SortedList Init ()
+               {
+                       var l = new SortedList ();
+                       l.Add ("key1", "banana");
+                       l.Add ("key2", "apple");
+                       return l;
+               }
+       }
+       
+       [DataContract]
+       public class StackContainer : CollectionContainer<Stack> {
+               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<EnumerableWithAdd>
+       {
+               protected override EnumerableWithAdd Init()
+               {
+                       var s = new EnumerableWithAdd();
+                       s.Add ("banana");
+                       s.Add ("apple");
+                       return s;
+               }
+       }
+
+       [DataContract]
+       public class EnumerableWithSpecialAddContainer : CollectionContainer<EnumerableWithSpecialAdd>
+       {
+               protected override EnumerableWithSpecialAdd Init()
+               {
+                       var s = new EnumerableWithSpecialAdd();
+                       s.Add("banana", 0);
+                       s.Add("apple", 0);
+                       return s;
+               }
+       }
+
+       [DataContract]
+       public class GenericDictionaryContainer : CollectionContainer<Dictionary<string, string>> {
+               protected override Dictionary<string, string> Init ()
+               {
+                       var d = new Dictionary<string, string> ();
+                       d.Add ("key1", "banana");
+                       d.Add ("key2", "apple");
+                       return d;
+               }
+       }
+
+       [DataContract]
+       public class GenericHashSetContainer : CollectionContainer<HashSet<string>> {
+               protected override HashSet<string> Init ()
+               {
+                       return new HashSet<string> { "banana", "apple" };
+               }
+       }
+
+       [DataContract]
+       public class GenericLinkedListContainer : CollectionContainer<LinkedList<string>> {
+               protected override LinkedList<string> Init ()
+               {
+                       var l = new LinkedList<string> ();
+                       l.AddFirst ("apple");
+                       l.AddFirst ("banana");
+                       return l;
+               }
+       }
+
+       [DataContract]
+       public class GenericListContainer : CollectionContainer<List<string>> {
+               protected override List<string> Init ()
+               {
+                       return new List<string> { "banana", "apple" };
+               }
+       }
+
+       [DataContract]
+       public class GenericQueueContainer : CollectionContainer<Queue<string>> {
+               protected override Queue<string> Init ()
+               {
+                       var q = new Queue<string> ();
+                       q.Enqueue ("banana");
+                       q.Enqueue ("apple" );
+                       return q;
+               }
+       }
+
+       [DataContract]
+       public class GenericSortedDictionaryContainer : CollectionContainer<SortedDictionary<string, string>> {
+               protected override SortedDictionary<string, string> Init ()
+               {
+                       var d = new SortedDictionary<string, string> ();
+                       d.Add ("key1", "banana");
+                       d.Add ("key2", "apple");
+                       return d;
+               }
+       }
+
+       [DataContract]
+       public class GenericSortedListContainer : CollectionContainer<SortedList<string, string>> {
+               protected override SortedList<string, string> Init ()
+               {
+                       var d = new SortedList<string, string> ();
+                       d.Add ("key1", "banana");
+                       d.Add ("key2", "apple");
+                       return d;
+               }
+       }
+
+       [DataContract]
+       public class GenericStackContainer : CollectionContainer<Stack<string>> {
+               protected override Stack<string> Init ()
+               {
+                       var s = new Stack<string> ();
+                       s.Push ("banana");
+                       s.Push ("apple" );
+                       return s;
+               }
+       }
+
+       public class GenericEnumerableWithAdd : IEnumerable<string>
+       {
+               private List<string> items;
+
+               public GenericEnumerableWithAdd()
+               {
+                       items = new List<string>();
+               }
+
+               IEnumerator IEnumerable.GetEnumerator()
+               {
+                       return items.GetEnumerator ();
+               }
+
+               public IEnumerator<string> GetEnumerator()
+               {
+                       return items.GetEnumerator ();
+               }
+
+               public void Add(string value)
+               {
+                       items.Add(value);
+               }
+
+               public int Count
+               {
+                       get {
+                               return items.Count;
+                       }
+               }
+       }
+
+       public class GenericEnumerableWithSpecialAdd : IEnumerable<string>
+       {
+               private List<string> items;
+
+               public GenericEnumerableWithSpecialAdd()
+               {
+                       items = new List<string>();
+               }
+
+               IEnumerator IEnumerable.GetEnumerator()
+               {
+                       return items.GetEnumerator ();
+               }
+
+               public IEnumerator<string> 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<GenericEnumerableWithAdd>
+       {
+               protected override GenericEnumerableWithAdd Init()
+               {
+                       var s = new GenericEnumerableWithAdd();
+                       s.Add ("banana");
+                       s.Add ("apple");
+                       return s;
+               }
+       }
+
+       [DataContract]
+       public class GenericEnumerableWithSpecialAddContainer : CollectionContainer<GenericEnumerableWithSpecialAdd>
+       {
+               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