【问题标题】:Supporting ISupportInitialize using json.net使用 json.net 支持 ISupportInitialize
【发布时间】:2016-01-18 12:36:11
【问题描述】:

我正在使用 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; }
    }

感谢您为我指明正确方向的任何帮助

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案1】:

    如果您不想手动将调用BeginInit()EndInit()[OnDeserializing] and [OnDeserialized] 回调添加到您的每个ISupportInitialize 类型,您可以创建DefaultContractResolver 的自定义子类,它会自动调用适当的方法:

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

    【讨论】:

      【解决方案2】:

      我猜你不需要那个。您可以尝试使用适当的设置进行序列化:

      // 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); 
      

      要调用您的 Initializ 方法,您可以尝试使用 JSON.NET 的 SerializationCallbacks

      public class ToBeSerialized
      {
          // [...]
      
          // this is probably superflous if the callbacks do what you want, you can move the code there
          public SupportsInitialize SupportsInitialize { get; set; }
      
          [OnDeserializing]
          internal void OnDeserializingMethod(StreamingContext context)
          {
              this.SupportsInitialize.BeginInit();
          }
      
          [OnDeserialized]
          internal void OnDeserializedMethod(StreamingContext context)
          {
              this.SupportsInitialize.EndInit();
          }
      }
      

      【讨论】:

      • 谢谢@nozzleman。我的 ISupportInitialize 中的实现不仅仅是恢复循环引用(不幸的是)。我还尝试了您的 sn-p 与父子关系。它们相互引用的地方,它不会完全恢复循环引用。孩子的父母将为空。
      • 为了让 JSON.NET 工作,需要有一个默认(无参数)ctor(可以是私有的)。尝试这样做以使深度循环工作。至于“其他事情”,请更新您的任务以明确您正在尝试做什么以及您失败的地方
      • 是的,我就是这么想的它重现了这个问题。
      • 我会在我的模型上检查一下,然后告诉你循环引用的效果。
      • 关于回调。我见过它们,但由于我无论如何都要包装序列化程序,所以我不妨在那里检查 ISupportInitialize 并调用相应的方法。我希望的是一种注册回调或子类化序列化程序的方法,以便我可以在那里进行检查,因此如果有人在模型深处添加了一个 ISupportInitialize 属性,则会更加开放。
      猜你喜欢
      • 1970-01-01
      • 2012-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多