skbkontur / GroBuf

Fast binary serializer
MIT License
64 stars 10 forks source link

Exception trying to serialize LinearGradientBrush #26

Closed VitorKawao closed 2 years ago

VitorKawao commented 3 years ago

When I try to serialize a LinearGradientBrush the GroBuf throw an exception: "nable to cast object of type 'System.Windows.Media.LinearGradientBrush' to type 'MS.Utility.FrugalObjectList`1[System.Windows.Freezable+FreezableContextPair]'." I am using this code:

Serializer _serializer = new Serializer(new AllPropertiesExtractor(), options: GroBufOptions.WriteEmptyObjects); LinearGradientBrush linGrBrush = new LinearGradientBrush(); linGrBrush.GradientStops.Add(new GradientStop(Colors.Red, 0.5)); linGrBrush.GradientStops.Add(new GradientStop(Colors.Blue, 1));

byte[] bytes = _serializer.Serialize(linGrBrush);

The Exception image

Anton92nd commented 2 years ago

What makes you think that it's correct to use instance of this class after serialize-deserialize operation? Do you have an example of another serializer that correctly handles this type?

GroBuf is intended to use with so called data transfer objects (DTOs) - classes that don't have any methods but have public fields or get/set properties of basic C# types, arrays of these types and classes of the same simple structure. Grobuf also works fine with commonly used collections such as List<T>, HashSet<T> and Dictionary<TK, TV>.

VitorKawao commented 2 years ago

I am using the basic DataContract from System.Runtime.Serialization (it works there). I was trying to change it for GroBuf

Anton92nd commented 2 years ago

I've tried to reproduce your case:

        [Test]
        public void TestBrush()
        {
            var brush = new LinearGradientBrush(new Color(), new Color(), new Point(2, 3), new Point(3, 4));
            var bytes = serializer.Serialize(brush);
            var deserializedBrush = serializer.Deserialize<LinearGradientBrush>(bytes);
            Console.WriteLine(deserializedBrush.EndPoint);
        }

and got the Exception

System.InvalidOperationException : Instance is readonly.
   in System.Globalization.NumberFormatInfo.VerifyWritable()
   in System.Globalization.NumberFormatInfo.set_CurrencyGroupSizes(Int32[] value)
   in Set_NumberFormatInfo_CurrencyGroupSizes_698fb9ea-37ea-42b9-93ce-86b971727810(IntPtr , Int32& , NumberFormatInfo& , ReaderContext )
   in Read_NumberFormatInfo_c5df3e6f-0863-47c3-b763-be8a5bfe9eb6(IntPtr , Int32& , NumberFormatInfo& , ReaderContext )
   in Set_CultureInfo_NumberFormat_09f20700-4d83-4b70-95f0-362fca70a7d2(IntPtr , Int32& , CultureInfo& , ReaderContext )
   in Read_CultureInfo_3a629877-bd29-4222-876a-f9892a484958(IntPtr , Int32& , CultureInfo& , ReaderContext )
   in Set_Thread_CurrentUICulture_8ce180c7-abda-4306-ab4b-5ffcb027e222(IntPtr , Int32& , Thread& , ReaderContext )
   in Read_Thread_85ed7c33-8144-4d50-8743-78d2e80d9fe0(IntPtr , Int32& , Thread& , ReaderContext )
   in Set_Dispatcher_Thread_d4dd1996-2d17-4a26-8f2f-bfaee9b01f96(IntPtr , Int32& , Dispatcher& , ReaderContext )
   in Read_Dispatcher_8606a71f-518c-4d0c-b6ee-cc1de1474989(IntPtr , Int32& , Dispatcher& , ReaderContext )
   in Set_GradientStopCollection_Dispatcher_0b002dfa-b3cc-464b-afbb-027cdef6b4e3(IntPtr , Int32& , GradientStopCollection& , ReaderContext )
   in Read_GradientStopCollection_fb40c43e-ed3a-48d0-8a3b-bcd6985d5ec6(IntPtr , Int32& , GradientStopCollection& , ReaderContext )
   in Set_LinearGradientBrush_GradientStops_09108799-1609-446c-87d6-813beeb438b4(IntPtr , Int32& , LinearGradientBrush& , ReaderContext )
   in Read_LinearGradientBrush_e0defe6d-b579-4656-805d-0af981ff5066(IntPtr , Int32& , LinearGradientBrush& , ReaderContext )
   in 48d2391a-4104-4509-98c4-8130a1b3e4f3(IntPtr , Int32& , LinearGradientBrush& , ReaderContext )
   in GroBuf.GroBufReader.Read[T](IntPtr data, Int32& index, Int32 length, T& result) in C:\workspace\GroBuf\GroBuf\GroBufReader.cs:line 98
   in GroBuf.GroBufReader.Read[T](Byte[] data, T& result) in C:\workspace\GroBuf\GroBuf\GroBufReader.cs:line 45
   in GroBuf.GroBufReader.Read[T](Byte[] data) in C:\workspace\GroBuf\GroBuf\GroBufReader.cs:line 75
   in GroBuf.Serializer.Deserialize[T](Byte[] data) in C:\workspace\GroBuf\GroBuf\Serializer.cs:line 50
   in GroBuf.Tests.IntegrationTest.TestBrush() in C:\workspace\GroBuf\GroBuf.Tests\IntegrationTest.cs:line 29

It turns out brush was successfully serialized, but cannot be deserialized because of System.Threading.Dispatcher object accessible through public properties (brush.GradientStops.Dispatcher in this stacktrace, but also through brush.Dispatcher). Dispatcher provides services for managing the queue of work items for a specific thread, so it contains information useful only at runtime and it is not meant to be serialized (stack trace leads us to CultureInfo property, but I don't think it is the biggest problem in this class).

I suppose DataContract serialization works because it only considers properties marked with specific attributes. Even so, I am not sure that in this case deserialized object works as it should work.