Я использую BinaryFormatter
для выполнения двоичной сериализации некоторых объектов в С#. Однако некоторые объекты содержат классы, к которым я обращаюсь через DLL, и у меня нет исходного кода, поэтому я не могу пометить их атрибутом Serializable
. Есть ли простой способ их сериализации? У меня есть обходное решение, которое включает в себя принятие класса NoSource
и создание нового класса SerializableNoSource
, для которого конструктор принимает объект NoSource
и извлекает всю необходимую мне информацию, но он взломан. Есть ли лучшие альтернативы?
Можно ли выполнять бинарную сериализацию .NET в случае, если у вас нет исходного кода класса?
Ответ 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);
}
}
Использование суррогата сериализации далеко не просто и может стать довольно многословным, когда тип, который вы пытаетесь выполнить сериализацию, имеет частные и защищенные поля, которые должны быть сериализованы.
Но поскольку вы уже вручную сериализуете нужные значения, я не думаю, что это проблема. Использование суррогата - более унифицированный способ справиться с подобным сценарием и сделать вас более комфортным.
Ответ 2
Возможно, вы сможете использовать Mono.Cecil, чтобы добавить [SerializableAttribute]
в классы, но я бы этого не сделал, если существует еще один способ достижения желаемого результата.
Ответ 3
Я согласен с @Servy, если автор класса не ожидал, что он будет сериализован, вы не должны пытаться его сериализовать напрямую. Таким образом, вы делаете правильные вещи с архитектурной точки зрения. Чтобы сделать ваш текущий подход меньше, "взломанный", рассмотрите возможность использования ISerializable для классов, содержащих ссылки на несериализуемые объекты.
Ответ 4
Создайте новый класс, наследуйте существующий класс, который не помечен атрибутом сериализации и не реализует интерфейс ISerializable.
Если класс запечатан, вы можете использовать Json.NET, а затем преобразовать его в двоичный и наоборот (Sucks большое время, используйте его, если ничего не может помочь:)).
Ответ 5
Я думаю, что более чистый способ заключается в том, чтобы внедрить интерфейс ISerializable и управлять сериализацией и обратным процессом. В MSDN мы можем найти:
Сериализацияне может быть добавлена в класс после того, как она была скомпилирован....