Is it possible to do .NET binary serialization of an object when you don't have the source code of the class?

后端 未结 6 1093
星月不相逢
星月不相逢 2020-11-27 04:52

I am using BinaryFormatter to do binary serialization of some objects in C#. However, some of the objects contain classes that I access via a DLL and do not hav

相关标签:
6条回答
  • 2020-11-27 04:55

    You could create a serialization surrogate.

    Imagine that we have a class defined in a referenced assembly that we have no control over that looks like this:

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public DriversLicense License;
    }
    
    
    // An instance of this type will be part of the object graph and will need to be 
    // serialized also.
    public class DriversLicense
    {
        public string Number { get; set; }
    }
    

    In order to serialize this object you will need to define a serialization surrogate for each type in the object graph.

    To create a serialization surrogate you simply need to create a type that implements the ISerializationSurrogate interface:

    public class PersonSurrogate : ISerializationSurrogate
    {
        /// <summary>
        /// Manually add objects to the <see cref="SerializationInfo"/> store.
        /// </summary>
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
            Person person = (Person) obj;
            info.AddValue("Name", person.Name);
            info.AddValue("Age", person.Age);
            info.AddValue("License", person.License);
        }
    
        /// <summary>
        /// Retrieves objects from the <see cref="SerializationInfo"/> store.
        /// </summary>
        /// <returns></returns>
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
        {
            Person person = (Person)obj;
            person.Name = info.GetString("Name");
            person.Age = info.GetInt32("Age");
            person.License = (DriversLicense) info.GetValue("License", typeof(DriversLicense));
            return person;
        }
    }
    
    public class DriversLicenseSurrogate : ISerializationSurrogate
    {
        /// <summary>
        /// Manually add objects to the <see cref="SerializationInfo"/> store.
        /// </summary>
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
            DriversLicense license = (DriversLicense)obj;
            info.AddValue("Number", license.Number);
        }
    
        /// <summary>
        /// Retrieves objects from the <see cref="SerializationInfo"/> store.
        /// </summary>
        /// <returns></returns>
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
        {
            DriversLicense license = (DriversLicense)obj;
            license.Number = info.GetString("Number");
            return license;
        }
    }
    

    Then you need to let your IFormatter know about the surrogates by defining and initializing a SurrogateSelector and assigning it to your IFormatter.

    private static void SerializePerson(Person person)
    {
        if (person == null)
            throw new ArgumentNullException("person");
    
        using (var memoryStream = new MemoryStream())
        {
            //Configure our surrogate selectors.
            var surrogateSelector = new SurrogateSelector();
            surrogateSelector.AddSurrogate(typeof (Person), new StreamingContext(StreamingContextStates.All),
                                           new PersonSurrogate());
            surrogateSelector.AddSurrogate(typeof (DriversLicense), new StreamingContext(StreamingContextStates.All),
                                           new DriversLicenseSurrogate());
    
            //Serialize the object
            IFormatter formatter = new BinaryFormatter();
            formatter.SurrogateSelector = surrogateSelector;
            formatter.Serialize(memoryStream, person);
    
            //Return to the beginning of the stream
            memoryStream.Seek(0, SeekOrigin.Begin);
    
            //Deserialize the object
            Person deserializedPerson = (Person) formatter.Deserialize(memoryStream);
        }
    }
    

    Using a serialization surrogate is by no means straightforward, and can actually become quite verbose when the type you are trying to serialize has private & protected fields that need to be serialized.

    But as you are already manually serializing the values you need, I don't think that is an issue. The use of a surrogate is a more unifom way of handling a scenario like this and should make you feel more comfortable.

    0 讨论(0)
  • 2020-11-27 04:55

    I agree with @Servy, if the author of the class did not anticipate that it would be serialized, you should not attempt to serialize it directly. So you're doing the right thing from an architectural standpoint. To make your current approach less, "hacky," consider implementing ISerializable for classes that contain references to non-serializable objects.

    0 讨论(0)
  • 2020-11-27 04:55

    I know this is an old question but I recently found the need to serialize/deserialize DTOs that we have no control over its source code in binary format for performance reasons. Managed to find quite a few alternate binary serializers such as ZeroFormatter and Protobuf but all of them require decorating the DTOs with attributes or define a schema.

    That led me down the path of creating my own binary serializer that is a quick replacement for JSON serializer in binary format which you may find useful: https://github.com/zachsaw/Binaron.Serializer

    0 讨论(0)
  • 2020-11-27 04:59

    I think the cleaner way would be to implenment ISerializable Interface and mange yourself the serialization and the reverse process. In MSDN we can find :

    serialization cannot be added to a class after it has been compiled....

    0 讨论(0)
  • 2020-11-27 05:11

    Create a new class, inherit the existing class that is not marked with serialization attribute and implement ISerializable interface.

    If the class is sealed then you can use Json.NET and then convert it to binary and vice versa (Sucks big time, use it if nothing else can help :)).

    0 讨论(0)
  • 2020-11-27 05:13

    You might be able to use Mono.Cecil to add the [SerializableAttribute] to the classes, but I wouldn't do it if there is another way of achieving the desired result.

    0 讨论(0)
提交回复
热议问题