Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
131 views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...