Standard: Cannot serialize System.Globalization.NumberFormatInfo into binary stream

Created on 23 Oct 2019  路  12Comments  路  Source: dotnet/standard

Hey there,

I was trying to serialize NumberFormatInfo into a binary stream using the following code:

`

        var locale = user?.Locale;
        var cultureInfo = new System.Globalization.CultureInfo(locale);

        using (var ms = new MemoryStream())
        {
            new BinaryFormatter().Serialize(ms, cultureInfo.NumberFormat);
            return ms.ToArray();
        }

`

It worked when we were using mscorlib 4.0.0 but not after migrating to dotnet standard. It looks like it used to have the Serialization attribute in for that class but in dotnet standard those attributes seems to be missing.

Was it intentional to make the NumberFormatInfo not serializable? Thanks!

Most helpful comment

@KarloX2 if you have control over the class derived of the System.ComponentModel.Component, you can implement the ISerializable interface in this class and manually serialize the NFI fields in ISerializable.GetObjectData and deserialize these fields from the serialization constructor.

We are trying to not make such classes serializable because in one hand we are trying not to promote BinaryFormatter for different reasons and in the other hand making them serializable will put a hard restriction on the object graphs (like ensuring private and internal field names cannot change, we'll have to support the deserialization from old objects created on the full framework and ensure the object we serialize can be deserialized on the full framework,...etc.).

All 12 comments

Same question here!
@devon94: Have you been able to work around this issue somehow?

cc: @tarekgh

Was it intentional to make the NumberFormatInfo not serializable? Thanks!

Yes, that was intentional. In general, in .Net Core, we don't mark all types that used to be serializable with the serialization attributes. We do so only for the scenarios that need to have the types serializable. NumberFormatInfo is not really interesting to serialize. What scenario do you need to do that? Thanks.

I have a class in .Net fw classic that has a field of that type NumberFormatInfo that is used to store rules for - surprise - number formatting and the entire objects of that class can be serialized and deserialized, including that NumberFormatInfo field. That used to work just fine but is now broken on .Net Core.

@KarloX2 we don't recommend using BinaryFormatter serialization. Would you try using some other serializer like DateContractSerilizer? it should be easy and simple too.

```C#

[DataContract(Name = "SerializerTest")]
public class SerializerTest
{
    [DataMember()]
    public NumberFormatInfo NFI {get; set;}

    public SerializerTest()
    {
        NFI = CultureInfo.InvariantCulture.NumberFormat;
    }
}

class Program
{

    static void Main(string[] args)
    {
        SerializerTest obj = new SerializerTest();
        Console.WriteLine(obj.NFI.CurrencySymbol);

        using (MemoryStream ms = new MemoryStream())
        {
            DataContractSerializer ser = new DataContractSerializer(typeof(SerializerTest));
            ser.WriteObject(ms, obj);
            ms.Seek(0, SeekOrigin.Begin);

            using (XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(ms, new XmlDictionaryReaderQuotas()))
            {
                ser = new DataContractSerializer(typeof(SerializerTest));
                SerializerTest obj1 = (SerializerTest) ser.ReadObject(reader, true);

                Console.WriteLine(obj1.NFI.CurrencySymbol);
            }
        }
    }
}

```

CC @GrabYourPitchforks

The types in the _System.Globalization_ namespace are generally not serializable in .NET Core. See https://github.com/dotnet/coreclr/issues/23567 for some further discussion. If you need to round-trip culture information, the recommendation is to have the sender write out just the culture identifier (e.g., the string "en-US" or "fr-FR") and to have the receiver call CultureInfo.GetCultureInfo.

In my case the NumberFormatInfo field is member of a class that in turn is part as a property of a class derived from System.ComponentModel.Component which can be used (as a non-visual component) in WinForms and WPF applications, usually placed on a component class file pane and there can be added from the tool box and configured with the property editor etc.

The NumberFormatInfo is not necessarily taken from a CultureInfo but can be configured independently.

I think for using this member in the WinForms designer I need it to support BinaryFormatter serialization, right? Easiest would be if NumberFormatInfo would just be tagged with [Serializable], but it isn't. Can I somehow inject that attribute? Otherwise, I think I would need to create a wrapper that has all the settings housing in NumberFormatInfo and can be serialized. Any other ideas?

@KarloX2 if you have control over the class derived of the System.ComponentModel.Component, you can implement the ISerializable interface in this class and manually serialize the NFI fields in ISerializable.GetObjectData and deserialize these fields from the serialization constructor.

We are trying to not make such classes serializable because in one hand we are trying not to promote BinaryFormatter for different reasons and in the other hand making them serializable will put a hard restriction on the object graphs (like ensuring private and internal field names cannot change, we'll have to support the deserialization from old objects created on the full framework and ensure the object we serialize can be deserialized on the full framework,...etc.).

This scenario should also be possible using a SurrogateSelector. When you create your BinaryFormatter instance, you can use a SurrogateSelector to redirect the serialization logic to your own type. This would allow you to iterate over a NumberFormatInfo instance and write all of its properties to the serialization stream, then rehydrate the type and set the relevant properties on deserialization.

This technique should work for any type where you need custom control over the serialization process. So if there are types beyond NumberFormatInfo which are giving you grief, you'd be able to apply this same technique to those types to unblock your scenario.

In the case of the WinForms designer, do I have control over the serialization process so I can add a custom SurrogateSelector instance?

I don't think you can control the designer serialization process, but your idea of creating a wrapper would solve the problem.

Seems resolved.

Was this page helpful?
0 / 5 - 0 ratings