c# - 如何以重构安全的方式使用 JSON.Net 来获取持久数据

标签 c# json refactoring migration json.net

我正在使用 JSON.Net 保存从接口(interface)继承的对象集合。我发现我可以设置 TypeNameHandling

JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

我可以获得注入(inject)到发出的 json 中的对象的完全限定类型名称,该名称可用于序列化以构造正确的对象。

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

但是,我对 resharper 重构非常满意,而且这种映射会在代码版本之间快速中断。

我想要的是,我不想存储完全限定的类型名,而是为每个类分配一个 Guid 作为类型 id,它在重构下永远不会改变,并使用它映射回反序列化代码。

这是有人以前尝试过的吗?如果是的话,该如何去做?

最佳答案

编辑

我们现在有一个开源项目来帮助迁移。

https://github.com/Weingartner/Migrations.Json.Net

原始答案

这是我为解决我的问题而编写的 JsonConverter。我所做的是使用 Guid 属性(通常用于 com 互操作)来标记我的序列化。然后我根据指南提供了一个工厂。该工厂基于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;
    }
}

关于c# - 如何以重构安全的方式使用 JSON.Net 来获取持久数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24179663/

相关文章:

python - 填充预定义的 pandas 数据框

c# - ASP .NET Webhook 接收器

design-patterns - 我该如何重构这个不幸的部分类?

c# - 静态只读字段的初始化顺序

c# - 返回图像的 ASPX - 输出可缓存?

c# - log4net 日志记录不创建日志文件

java - 从 Spotify End Point 获取所需的图像

javascript - 过滤一系列控件并操作它们

java - 处理这个问题的正确设计是什么?

c# - 从 MethodCallExpression 访问调用对象