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

6

26

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 have the source code for, so I can't mark them with the Serializable attribute. Is there a straightforward way to serialize them anyway? I have a workaround which involves taking class NoSource and making a new class SerializableNoSource for which the constructor takes a NoSource object and extracts all the information I need from it, but it's hacky. Are there any better alternatives?

Unfavorable answered 31/10, 2012 at 19:24 Comment(1)
For the record, encapsulating the object in another object that does know how to serialize itself as well as converting to/from the encapsulated object isn't hacky in my mind, it's entirely appropriate. Now, there could be something slicker or better out there, but I wouldn't consider your current approach fundamentally flawed.Inguinal
A
29

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.

Autoxidation answered 20/4, 2013 at 14:21 Comment(3)
This appears to be the textbook answer to truly serialize an unowned class that is not marked [Serializable]Nissa
If you want more information you can read chapter 23 of CLR via C# - Runtime Serialization.Autoxidation
Also found this relevant article on MSDN: msdn.microsoft.com/en-us/magazine/cc188950.aspxNissa
C
1

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.

Charr answered 31/10, 2012 at 20:4 Comment(0)
S
0

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.

Schoolfellow answered 31/10, 2012 at 20:13 Comment(0)
K
0

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 :)).

Kape answered 31/10, 2012 at 23:23 Comment(0)
C
0

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

Confident answered 10/12, 2019 at 11:50 Comment(0)
S
-2

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....

Scleroderma answered 31/10, 2012 at 19:45 Comment(3)
"However, some of the objects contain classes that I access via a DLL and do not have the source code for"Plotkin
You can I think create your own classe which could have the same properties as the object you don't have the source No ? Or extends it??Scleroderma
Extending won't work, copying the properties... that's the same as what he has nowPlotkin

© 2022 - 2024 — McMap. All rights reserved.