Как использовать JSON.Net безопасным способом рефакторинга для постоянных данных

1

Я использую JSON.Net для сохранения коллекций объектов, наследуемых от интерфейса. Я обнаружил, что могу установить TypeNameHandling, как в

JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

и я могу получить полное имя типа объектов, введенных в испускаемый json, которые можно использовать для сериализации для построения правильного объекта.

"$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests"

Тем не менее, я довольно счастлив с помощью моего рефакторинга resharper, и такое сопоставление быстро разрывается между версиями кода.

То, что я хотел бы, вместо того, чтобы хранить полностью квалифицированное имя-типа, я бы присвоил каждому классу Guid как идентификатор типа, который никогда не изменится при рефакторинге и не использует его для отображения кода десериализации.

Это то, что кто-то пробовал раньше, и если да, то как это сделать?

Теги:
migration
json.net
refactoring

1 ответ

2

РЕДАКТИРОВАТЬ

Теперь у нас есть проект с открытым исходным кодом, который поможет в миграции.

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;
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню