C# 使用json.net支持ISupportInitialize

C# 使用json.net支持ISupportInitialize,c#,json,serialization,json.net,C#,Json,Serialization,Json.net,我正在使用Newtonsoft json.net序列化程序将我的一些模型序列化为json。 其中一些是复杂的,包含循环引用。由于循环引用不可序列化,因此将通过设置ReferenceLoopHandling=ReferenceLoopHandling.Ignore处理这些循环引用。 在使用ISupportInitialize进行其他序列化之后,将还原引用。 ISupportInitialize还用于从数据库初始化重属性(图像)。有些类没有默认的ctor(需要[NotNull]参数),我知道我的重载

我正在使用Newtonsoft json.net序列化程序将我的一些模型序列化为json。 其中一些是复杂的,包含循环引用。由于循环引用不可序列化,因此将通过设置
ReferenceLoopHandling=ReferenceLoopHandling.Ignore
处理这些循环引用。 在使用ISupportInitialize进行其他序列化之后,将还原引用。 ISupportInitialize还用于从数据库初始化重属性(图像)。有些类没有默认的ctor(需要[NotNull]参数),我知道我的重载属性可能是惰性的,但由于我的ISupportInitialize在以前的序列化程序中运行良好(并且经过测试),我更愿意使用这种机制

我试图让json.net序列化程序检测实现ISupportInitialize接口的类型,并调用适当的接口方法。当实际的序列化/反序列化对象实现接口时,这很简单,但当对象图中的某个属性实现接口时,这会更困难(或者至少对我来说不是直观的)。我试着写一个自定义转换器,但运气不好。下面是我想完成的一个简单例子

 public class SupportsInitialize : ISupportInitialize
    {
        public void BeginInit(){throw new NotImplementedException();}

        public void EndInit(){throw new NotImplementedException();}
    }

    [Test]
    public void MakeSerializerCallBeginAndEndInit()
    {
        var supportsInitialize = new ToBeSerialized() {SupportsInitialize = new SupportsInitialize()};
        // before serializing any property implementing ISupportInitialize (ToBeSerialized.SupportsInitialize in this case) its BeginInit should be called
        var json = JsonConvert.SerializeObject(supportsInitialize);
        // after deserializing any property implementing ISupportInitialize its EndInit should be called
        var deserialized = JsonConvert.DeserializeObject<ToBeSerialized>(json); 
    }

    public class ToBeSerialized
    {
        public SupportsInitialize SupportsInitialize { get; set; }
    }
公共类支持初始化:ISupportInitialize
{
public void BeginInit(){抛出新的NotImplementedException();}
public void EndInit(){抛出新的NotImplementedException();}
}
[测试]
public void makeSerializerCallBeginAndInIT()
{
var supportsInitialize=new ToBeSerialized(){supportsInitialize=new supportsInitialize()};
//在序列化任何实现ISupportInitialize(本例中为ToBeSerialize.SupportsInitialize)的属性之前,应调用其BeginInit
var json=JsonConvert.SerializeObject(supportsInitialize);
//反序列化任何实现ISupportInitialize的属性后,应调用其EndInit
var deserialized=JsonConvert.DeserializeObject(json);
}
公共类将被私有化
{
公共支持Sinitialize支持Sinitialize{get;set;}
}

谢谢你给我指出正确方向的任何帮助我想你不需要这个。您可以尝试使用适当的设置序列化:

// intended is not needed, but it makes it easier to know whats going on.
var json = JsonConvert.SerializeObject(yourObject, Formatting.Indented, new JsonSerializerSettings
    {
        ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
        PreserveReferencesHandling = PreserveReferencesHandling.Objects
    });
并像往常一样反序列化:

var obj = JsonConvert.DeserializeObject<ToBeSerialized>(json); 

如果您不想手动将调用
BeginInit()
EndInit()
的回调添加到每个类型中,您可以创建一个自定义子类,自动调用相应的方法:

public class ISupportInitializeContractResolver : DefaultContractResolver
{
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    static ISupportInitializeContractResolver instance;

    // Using a static constructor enables fairly lazy initialization.  http://csharpindepth.com/Articles/General/Singleton.aspx
    static ISupportInitializeContractResolver() { instance = new ISupportInitializeContractResolver(); }

    public static ISupportInitializeContractResolver Instance { get { return instance; } }

    readonly SerializationCallback onDeserializing;
    readonly SerializationCallback onDeserialized;

    protected ISupportInitializeContractResolver()
        : base()
    {
        onDeserializing = (o, context) =>
            {
                var init = o as ISupportInitialize;
                if (init != null)
                    init.BeginInit();
            };
        onDeserialized = (o, context) =>
            {
                var init = o as ISupportInitialize;
                if (init != null)
                    init.EndInit();
            };
    }

    protected override JsonContract CreateContract(Type objectType)
    {
        var contract = base.CreateContract(objectType);
        if (typeof(ISupportInitialize).IsAssignableFrom(objectType))
        {
            contract.OnDeserializingCallbacks.Add(onDeserializing);
            contract.OnDeserializedCallbacks.Add(onDeserialized);
        }
        return contract;
    }
}
然后像这样使用它:

        var settings = new JsonSerializerSettings { ContractResolver = ISupportInitializeContractResolver.Instance };
        var root = JsonConvert.DeserializeObject<ToBeSerialized>(jsonString, settings);
var settings=newjsonserializersettings{ContractResolver=ISupportInitializeContractResolver.Instance};
var root=JsonConvert.DeserializeObject(jsonString,设置);

谢谢@nozzleman。我的ISupportInitialize中的实现不仅仅是恢复循环引用(不幸的是)。我还尝试了你的片段与父母和孩子的关系。它们相互引用,并且不会完全恢复循环引用。Child的父项将为null。为了让JSON.NET正常工作,需要有一个默认(无参数)的构造函数(可以是私有的)。尝试这样做,使深循环工作。至于“其他事情”,请更新你的问题,以明确你想做什么以及你失败的地方是的,这就是我所想的,至于其他事情,我真的不确定你想实现什么,所以更新和明确你的问题是个好主意,maaybe提供了一个复制该问题的min示例。我将在我的模型上检查它,并让您知道它如何适用于循环引用。关于回调。我见过它们,但由于我正在包装序列化程序,所以我不妨在那里检查ISupportInitialize并调用相应的方法。我所希望的是一种注册回调或子类化序列化程序的方法,这样我就可以在那里进行检查,因此如果有人在模型的深处添加了一个ISupportInitialize属性,就会更加开放。
        var settings = new JsonSerializerSettings { ContractResolver = ISupportInitializeContractResolver.Instance };
        var root = JsonConvert.DeserializeObject<ToBeSerialized>(jsonString, settings);