Я использую JSON.Net для сохранения коллекций объектов, наследуемых от интерфейса. Я обнаружил, что могу установить TypeNameHandling, как в
JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
и я могу получить полное имя типа объектов, введенных в испускаемый json, которые можно использовать для сериализации для построения правильного объекта.
"$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests"
Тем не менее, я довольно счастлив с помощью моего рефакторинга resharper, и такое сопоставление быстро разрывается между версиями кода.
То, что я хотел бы, вместо того, чтобы хранить полностью квалифицированное имя-типа, я бы присвоил каждому классу Guid как идентификатор типа, который никогда не изменится при рефакторинге и не использует его для отображения кода десериализации.
Это то, что кто-то пробовал раньше, и если да, то как это сделать?
РЕДАКТИРОВАТЬ
Теперь у нас есть проект с открытым исходным кодом, который поможет в миграции.
https://github.com/Weingartner/Migrations.Json.Net
ОРИГИНАЛЬНЫЙ ОТВЕТ
Вот JsonConverter, который я написал, чтобы решить мою проблему. То, что я сделал, это использовать атрибут Guid (который обычно используется для com interop), чтобы пометить мою сериализацию. Затем я предоставил фабрику, основанную на руководстве. Завод был основан на основе ninject DI.
Критическая строка кода находится в методе ReadJson
var document = _Kernel.Get<IWeinCadDocument>(typeId.ToString());
здесь я создаю экземпляр моего документа правильного типа на основе строки Guid. Типы предварительно зарегистрированы с использованием следующей функции.
public void BindDocumentTypes(params Type[] types)
{
Debug.Assert
(types.All(p => typeof (IWeinCadDocument).IsAssignableFrom(p)));
foreach (var documentType in types)
{
Kernel.Bind<IWeinCadDocument>()
.To(documentType)
.Named(documentType.GUID.ToString());
}
}
которые могут быть использованы следующим образом для регистрации набора типов с помощью Guid.
BindDocumentTypes(
typeof (ADocument), typeof (BDocument), typeof (CDocument)
);
Чтобы убедиться, что Guid не меняется между версиями программного обеспечения или даже компиляторами, мы применяем Guid так же с атрибутом.
[Guid("4882176A-751A-4153-928A-915BEA87FAB3")]
public class ADocument : WeinCadDocumentBase<ADocument>
{
public ADocument( IWeinCadDocumentStorage storage )
: base(storage)
{
}
public override object PersistentData
{
get
{
return new DocumentData(10, 20);
}
}
}
полный JSonConverter ниже.
public class WeinCadDocumentConverter : JsonConverter
{
private readonly IKernel _Kernel;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var document = value as IWeinCadDocument;
Debug.Assert(document != null, "document != null");
AssertThatWeHaveACustomGuidSet(value);
writer.WriteStartObject();
writer.WritePropertyName("InstanceId");
writer.WriteValue(document.InstanceId);
writer.WritePropertyName("TypeId");
writer.WriteValue(document.GetType().GUID);
writer.WritePropertyName("Name");
writer.WriteValue(document.Name);
writer.WritePropertyName("Data");
serializer.Serialize(writer, document.PersistentData);
writer.WriteEndObject();
}
/// <summary>
/// The object need a custom GuidAttribute to be set on the class otherwise the
/// GUID may change between versions of the code or even runs of the applications.
/// This Guid is used for identifying types from the document store and calling
/// the correct factory.
/// </summary>
/// <param name="value"></param>
private static void AssertThatWeHaveACustomGuidSet(object value)
{
var attr = System.Attribute.GetCustomAttributes(value.GetType())
.Where(a => a is GuidAttribute)
.ToList();
if (attr.Count == 0)
throw new ArgumentException
(String.Format
(@"Type '{0}' does not have a custom GuidAttribute set. Refusing to serialize.",
value.GetType().Name),
"value");
}
public override object ReadJson
(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JObject json = JObject.Load(reader);
var props = json.Properties().ToList();
var instanceId = (Guid) props[0].Value;
var typeId = (Guid) props[1].Value;
var name = (string) props[2].Value;
var data = props[3].Value;
var document = _Kernel.Get<IWeinCadDocument>(typeId.ToString());
document.PersistentData = data;
document.InstanceId = instanceId;
document.Name = name;
return document;
}
public override bool CanConvert(Type objectType)
{
return typeof (IWeinCadDocument).IsAssignableFrom(objectType);
}
public WeinCadDocumentConverter(IKernel kernel)
{
_Kernel = kernel;
}
}