locked
System.Runtime.Serialization (MonoAndroid & MonoTouch) 2.0.5.0 is NOT working as expected RRS feed

  • Question

  • User15694 posted

    I can't make polymorphism to be respected by Xamarin DataContractJsonSerializer (System.Runtime.Serialization.Json) (System.Runtime.Serialization.dll 2.0.5.0) in MonoAndroid & MonoTouch. I attach 4 test projects - Win8, Windows phone 8, Xamarin.Android, and Xamarin.iOS, and one file with the test output of four projects. (Polymorphism is respected ONLY in Win8 & WP8). The serialization testing code is in Win8 project and linked (so - identical) between other three projects.

    The object graph is:

    TestBase string TestBaseString int TestBaseInt Test1:TestBase string Test1String int Test1Int
    Test2:TestBase string Test2String int Test2Int
    Test TestBase Test1 TestBase Test2 List TestList

    Then the properties in class Test are instantiated as such: Test1 is of type Test1, Test2 is of type Test2, TestList[0] is of type Test1, and TestList[1] is of type Test2.

    The problem is that after serializing & deserializing with Xamarin DataContractJsonSerializer the properties in class Test are downgraded to its base : all Test1, Test2, TestList[0], and TestList[1] are now of type TestBase.

    The full serialization testing code is:

    TestSerialization.cs:

    using System; using System.Collections.Generic; using System.Text;

    using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.IO;

    namespace Test { public class TestSerialization {

        public TestSerialization() { }
        public void Execute()
        {
            var test = new Test();
    
            Log("Original:");
            Log("   Test1:" + test.Test1.GetType());
            Log("   Test2:" + test.Test2.GetType());
            Log("   TestList[0]:" + test.TestList[0].GetType());
            Log("   TestList[1]:" + test.TestList[1].GetType());
    
            var testSerialized = Serialize(test);
            var testDeserialized = Deserialize<Test>(testSerialized);
    
            Log("Deserialized:");
            Log("   Test1:" + testDeserialized.Test1.GetType());
            Log("   Test2:" + testDeserialized.Test2.GetType());
            Log("   TestList[0]:" + testDeserialized.TestList[0].GetType());
            Log("   TestList[1]:" + testDeserialized.TestList[1].GetType());
    
        }
    
        public T Deserialize<T>(string json)
        {
            T deserilialized = default(T);
            try
            {
                var bytes = Encoding.Unicode.GetBytes(json);
                using (MemoryStream stream = new MemoryStream(bytes))
                {
                    var serializer = new DataContractJsonSerializer(typeof(T));
                    deserilialized = (T)serializer.ReadObject(stream);
                }
            }
            catch (Exception ex)
            {
                Log(ex.ToString());
            }
            return deserilialized;
        }
        public string Serialize(object instance)
        {
            string jsonString = null;
            try
            {
                using (MemoryStream stream = new MemoryStream())
                {
                    var serializer = new DataContractJsonSerializer(instance.GetType());
                    serializer.WriteObject(stream, instance);
                    stream.Position = 0;
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        jsonString = reader.ReadToEnd();
                    }
                }
            }
            catch (System.Exception ex)
            {
                Log(ex.ToString());
            }
            return jsonString;
        }
        public void Log(string info)
        {
            System.Diagnostics.Debug.WriteLine(info);
        }
    
    }
    
    [DataContract]
    [KnownType(typeof(Test1))]
    [KnownType(typeof(Test2))]
    public class TestBase
    {
        [DataMember]
        public string TestBaseString;
    
        [DataMember]
        public int TestBaseInt;
    
    
        public TestBase()
        {
            TestBaseString = "TestBaseString";
            TestBaseInt = -1;
        }
    }
    [DataContract]
    public class Test1 : TestBase
    {
        [DataMember]
        public string Test1String;
    
        [DataMember]
        public int Test1Int;
    
        public Test1()
        {
            Test1String = "Test1String";
            Test1Int = 1;
        }
    }
    [DataContract]
    public class Test2 : TestBase
    {
        [DataMember]
        public string Test2String;
    
        [DataMember]
        public int Test2Int;
    
        public Test2()
        {
            Test2String = "Test2String";
            Test2Int = 2;
        }
    }
    [DataContract]
    public class Test
    {
        [DataMember]
        public TestBase Test1;
    
        [DataMember]
        public TestBase Test2;
    
        [DataMember]
        public List<TestBase> TestList;
    
        public Test()
        {
            Test1 = new Test1();
            Test2 = new Test2();
    
            TestList = new List<TestBase>();
            TestList.Add(new Test1());
            TestList.Add(new Test2());
        }
    }
    

    }


    The output is:

    Win8: WP8: ----- ---- Remote Machine: OK Device : OK Local Machine : OK Simulator : OK

    Original: Test1:Test.Test1 Test2:Test.Test2 TestList[0]:Test.Test1 TestList[1]:Test.Test2 Deserialized: Test1:Test.Test1 Test2:Test.Test2 TestList[0]:Test.Test1 TestList[1]:Test.Test2

    Android:

    Visual Studio Premium 2012 (Version 11.0.60315.01 Update 2) Xamarin.Android 4.8.01013 (b76e8ec4) Added Reference: System.Runtime.Serialization.dll (2.0.5.0) C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v1.0\System.Runtime.Serialization.dll Device (Google Nexus 7) : NOT OK

    Original: Test1:Test.Test1 Test2:Test.Test2 TestList[0]:Test.Test1 TestList[1]:Test.Test2 Deserialized: Test1:Test.TestBase Test2:Test.TestBase TestList[0]:Test.TestBase TestList[1]:Test.TestBase

    iOS:

    Visual Studio Premium 2012 (Version 11.0.60315.01 Update 2) Xamarin.iOS 1.3.250 (49348a51) Added Reference: System.Runtime.Serialization.dll (2.0.5.0) C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoTouch\v4.0\System.Runtime.Serialization.dll

    From Visual Studio on iPad (Mac Host) : NOT OK From Xamarin Studio (Mac) on iPad : NOT OK

    Original: Test1:Test.Test1 Test2:Test.Test2 TestList[0]:Test.Test1 TestList[1]:Test.Test2 Deserialized: Test1:Test.TestBase Test2:Test.TestBase TestList[0]:Test.TestBase TestList[1]:Test.TestBase

    ======================================================================================

    Monday, August 19, 2013 12:55 AM

All replies

  • User15694 posted

    The attached zip is here:

    Monday, August 19, 2013 12:58 AM
  • User21208 posted

    Hello,

    I can confirm the problem – please find attached a simple Android project that demonstrates the incorrect DataContractJsonSerializer behavior.

    I’ve been researching a way to port our Windows Phone 8 and Windows Store applications having a lot of JSON-based assets and this is a real showstopper for us :-(. Please help.

    Regards,

    Palo

    #

    Here is the relevant code from the attached solution for easy reference:

      [Activity (Label = "XamarinJsonSerializationTest", MainLauncher = true)]
      public class MainActivity : Activity
      {
        int count = 1;
    
        protected override void OnCreate (Bundle bundle)
        {
          base.OnCreate (bundle);
    
          // Set our view from the "main" layout resource
          SetContentView (Resource.Layout.Main);
    
          // [snip]...
    
          // CategoryTypeData inherits the Id property from the base EntityDataBase class.
          var category = new CategoryTypeData () { Id = "100", Name = "Cat100" };
    
          // After serialization, the JSON string does NOT contain the inherited Id property...
          string json = JsonSerializer.SerializeObject (category);
    
          // ...therefore, after deserialization, the deserializaed CategoryTypeData instance
          // has the Id property uninitialized (NULL).
          var categoryClone = JsonSerializer.DeserializeObject<CategoryTypeData> (json);
    
          // The following assert always fails.
          System.Diagnostics.Debug.Assert (category.Id == categoryClone.Id);
        }
      }
    
    
      /// <summary>
      /// Simple JSON serialization helper.
      /// </summary>
      internal static class JsonSerializer
      {
        private readonly static Type[] _s_knownTypes = 
        {
          typeof(EntityDataBase), typeof(CategoryTypeData)
        }; 
    
    
        public static T DeserializeObject<T>(string json)
        {
          DataContractJsonSerializer serializer = JsonSerializer.CreateSerializer (typeof(T));
          using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
          {
            return (T)serializer.ReadObject (stream);
          }
        }
    
    
        public static string SerializeObject(object instance)
        {
          DataContractJsonSerializer serializer = JsonSerializer.CreateSerializer (instance.GetType ());
          using (var stream = new MemoryStream())
          {
            serializer.WriteObject(stream, instance);
            return Encoding.UTF8.GetString(stream.ToArray(), 0, (int)stream.Length);
          }
        }
    
    
        private static DataContractJsonSerializer CreateSerializer(Type type)
        {
          return new DataContractJsonSerializer(type, JsonSerializer._s_knownTypes);
        }
      }
    
    
      /// <summary>
      /// Base class for DataContract-serializable entities.
      /// </summary>
      [DataContract]
      public abstract class EntityDataBase
      {
        [DataMember]
        private string _id;
    
        public string Id
        {
          get { return this._id; }
          set { this._id = value; }
        }
      }
    
    
      /// <summary>
      /// Derived class inheriting the Id property.
      /// </summary>
      [DataContract]
      public class CategoryTypeData : EntityDataBase
      {
        [DataMember]
        private string _name;
    
        public string Name
        {
          get { return this._name; }
          set { this._name = value; }
        }
      }
    

    Here is my version information:

    === Xamarin Studio ===

    Version 4.0.12 (build 3) Installation UUID: 44f60dbd-5046-4fca-b912-024a594cf479 Runtime: Microsoft .NET 4.0.30319.18213 GTK 2.24.20 GTK# (2.12.0.0)

    === Xamarin.Android ===

    Version: 4.8.1 (Trial Edition) Android SDK: C:\Users\Palo\AppData\Local\Android\android-sdk Supported Android versions: 2.1 (API level 7) 2.2 (API level 8) 2.3 (API level 10) 3.1 (API level 12) 4.0 (API level 14) 4.0.3 (API level 15) Java SDK: C:\Program Files (x86)\Java\jdk1.6.039 java version "1.6.039" Java(TM) SE Runtime Environment (build 1.6.0_39-b04) Java HotSpot(TM) Client VM (build 20.14-b01, mixed mode, sharing)

    === Build Information ===

    Release ID: 400120003 593d7acb1cb78ceeeb482d5133cf1fe514467e39 Build date: 2013-08-07 18:31:48Z Xamarin addins: 25a0858b281923e666b09259ad4746b774e0a873

    === Operating System ===

    Windows 6.2.9200.0 (64-bit)

    Wednesday, September 11, 2013 6:59 AM
  • User21208 posted

    I wasn't able to attach the file (in Opera), so here is a link to it: (http://sdrv.ms/17YntSg)

    Wednesday, September 11, 2013 7:02 AM