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?
Answer 1:
您可以创建一个序列化代理 。
试想一下,我们有一个引用的程序集,我们有过不控制是这样定义的类:
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; }
}
为了序列化这个对象,你需要定义序列化代理的对象图中的每一种类型。
要创建您只需创建一个实现一个类型序列化代理ISerializationSurrogate
接口:
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;
}
}
然后,你需要让你的IFormatter
了解代理人通过定义和初始化SurrogateSelector
并将其分配给您的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);
}
}
使用序列化代理绝不是简单的,并且实际上可以变得相当冗长,当你试图序列类型有需要序列私人及保护领域。
但是,正如你已经手动序列化您需要的价值观,我不认为这是一个问题。 使用替代的是处理一个场景像这样的更unifom方式应该让你感觉更舒服。
Answer 2:
你也许能够使用Mono.Cecil能在添加[SerializableAttribute]
的类,但如果达不到预期结果的另一种方式,我不会去做。
Answer 3:
我同意@Servy,如果类的作者没有预料到它会被序列化,你不应该试图直接序列化。 所以你从架构的角度来看做正确的事。 为了使您目前的做法少,“哈克,”考虑实施了ISerializable对于包含非序列化对象的引用的类。
Answer 4:
创建一个新的类,继承未标有序列化属性,实现ISerializable接口现有的类。
如果类是密封的,那么你可以使用Json.NET,然后将其转换为二进制,反之亦然(吮吸大的时候,使用它,如果没有其他可以帮助:))。
Answer 5:
我认为,更清洁的方式将implenment ISerializable接口和疥癣自己的序列化和逆过程。 在MSDN我们可以发现:
序列化不能被添加到一个类已经编译后....
文章来源: Is it possible to do .NET binary serialization of an object when you don't have the source code of the class?