我正在使用BinaryFormatter
对C#中的某些对象进行二进制序列化。但是,某些对象包含我通过DLL访问但没有源代码的类,因此我无法使用Serializable
属性标记它们。是否有一种简单的方法来序列化它们?我有一个解决方法,涉及获取类NoSource
并创建一个新的类SerializableNoSource
,构造函数将为其提取NoSource
对象,并从中提取所需的所有信息,但它很糟糕。还有更好的选择吗?
答案 0 :(得分:24)
您可以创建序列化代理。
想象一下,我们在引用的程序集中定义了一个类,我们无法控制它,如下所示:
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);
}
}
使用序列化代理并不简单,当您尝试序列化的类型具有私有&amp;时,实际上可能会变得非常冗长。需要序列化的受保护字段。
但是,由于您已经手动序列化了所需的值,我认为这不是问题。使用代理是一种更加统一的处理这种情况的方式,应该让你感觉更舒服。
答案 1 :(得分:1)
您可以使用Mono.Cecil将[SerializableAttribute]
添加到类中,但如果有其他方法可以实现所需的结果,我就不会这样做。
答案 2 :(得分:0)
我同意@Servy,如果该类的作者没有预料到它会被序列化,那么你不应该尝试直接序列化它。所以从架构的角度来看,你做的是正确的事情。为了减少当前的方法,“hacky”,考虑为包含对非可序列化对象的引用的类实现ISerializable。
答案 3 :(得分:0)
创建一个新类,继承未标记序列化属性的现有类,并实现ISerializable接口。
如果课程是密封的,那么你可以使用Json.NET,然后将其转换为二进制,反之亦然(如果没有其他任何东西可以帮助的话,那就太大了,请使用它。)。
答案 4 :(得分:0)
我知道这是一个老问题,但最近我发现有必要对DTO进行序列化/反序列化,出于性能原因,我们无法以二进制格式对其源代码进行控制。设法找到了很多替代的二进制序列化器,例如ZeroFormatter和Protobuf,但它们都需要用属性修饰DTO或定义模式。
这使我无法创建自己的二进制序列化器,该二进制序列化器可以快速替代二进制格式的JSON序列化器,您可能会发现它有用:https://github.com/zachsaw/Binaron.Serializer
答案 5 :(得分:-2)
我认为更简洁的方法是实现ISerializable接口并自己管理序列化和反向过程。 在MSDN中,我们可以找到:
序列化后不能添加到类中 编译....