使用 json.net 在构造函数中使用接口参数反序列化不可变 类

Deserialize immutable classes with interface parameter in the constructor using json.net

我想用 Json.net 创建一些不可变的 classes,但遇到错误消息:“无法创建 SolutionName.InterfaceClassName.Type 类型的实例是接口或抽象 class 且无法实例化。

问题:

我有一个不可变的 class,实现了一些接口。

public interface ISubClass1
    {
        int number { get; }
        string string1 { get; }
    }

子class:

public class SubClass1 : ISubClass1
    {
        public int number { get; }
        public string string1 { get; }  

        public SubClass1(int a, string str1)
        {
            number = a;
            string1 = str1;
        }
    }

public class SubClass2 : ISubClass1
        {
            public int number { get; }
            public string string1 { get; }  
            public string string2 { get; }  

            public SubClass2(int a, string str1, string str2)
            {
                number = a;
                string1 = str1;
                string2 = str2;
            }
        }

实施:

class Class1
    {
        public int SomeInt{ get; }
        public ISubClass1 SomeInterface { get; }

        public Class1(int a, ISubClass1 subclass)
        {
            SomeInt= a;
            SomeInterface = subclass;
        }
    }

我可以序列化这个对象,一切正常。但是在反序列化的时候会报错

"Could not create an instance of type SolutionName.InterfaceClassName.Type is an interface or abstract class and cannot be instantiated"

这是由于ISubClass1 subclass无法被Json.net识别。

解决方法:

您将需要自定义 Json 转换器,以便您可以使用所需的具体类型解析 ISubclass1

1 - 将此通用转换器添加到您的解决方案中。

    public abstract class JsonCreationConverter<T> : JsonConverter
    {
        /// <summary>
        /// Create an instance of objectType, based properties in the JSON object
        /// </summary>
        /// <param name="objectType">type of object expected</param>
        /// <param name="jObject">
        /// contents of JSON object that will be deserialized
        /// </param>
        protected abstract T Create(Type objectType, JObject jObject);
    
        public override bool CanConvert(Type objectType)
        {
            return typeof(T).IsAssignableFrom(objectType);
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override object ReadJson(JsonReader reader,
                                        Type objectType,
                                         object existingValue,
                                         JsonSerializer serializer)
        {
            // Load JObject from stream
            JObject jObject = JObject.Load(reader);
    
            // Create target object based on JObject
            T target = Create(objectType, jObject);
    
            // Populate the object properties
            serializer.Populate(jObject.CreateReader(), target);
    
            return target;
        }
    }

2 - 然后像下面的例子一样继承任何你想翻译的接口。请注意,FieldExists 检查正在检查一个字段,该字段将标识 subclass 的类型。这样 Json 不需要 $type 信息即可正确反序列化。 参考:BulletProof Deserialization

    public class SubClass1Converter : JsonCreationConverter<ISubClass1>
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new InvalidOperationException("Use default serialization.");
        }
    
        protected override ISubClass1 Create(Type objectType, JObject jObject)
        {
            if (FieldExists("string2", jObject))
            {
                return new SubClass2();
            }
            else
            {
                return new SubClass1();
            }
        }
    
        private bool FieldExists(string fieldName, JObject jObject)
        {
            return jObject[fieldName] != null;
        }
    }

3 - 您已使用 Json.net 属性修改子classes 和实现,否则反序列化会将默认值设置为 null。你有一个不可变的 class 并实现了一些接口。

一些关键点:

  1. [JsonProperty] 需要出现在每个 属性 上,否则属性将被读取为 null;
  2. [public SubClass1() { }] 需要存在一个空的构造函数,以便转换器声明一个空的 class。
  3. `[JsonConstructor] ' 需要在构造函数上标记,允许属性在不可变对象中获取传递给它们的值。
  4. { get; } 需要更改为 { get; private set; } 否则所有属性都将设置为 null
  5. public ISubClass1 SomeInterface { get; private set;}在实现中需要标注[JsonConverter(typeof(SubClass1Converter))]属性。

亚class:

    public class SubClass1 : ISubClass1
        {
            [JsonProperty] 
            public int number { get; private set;}
            [JsonProperty]
            public string string1 { get; private set;} 

            public SubClass1() { }

            [JsonConstructor]
            public SubClass1(int a, string str1)
            {
                number = a;
                string1 = str1;
            }
        }

    public class SubClass2 : ISubClass1
            {
                [JsonProperty] 
                public int number { get; private set;}
                [JsonProperty]
                public string string1 { get; private set;} 
                [JsonProperty]
                public string string2 { get; private set;}  

                public SubClass2() { }

                [JsonConstructor]
                public SubClass2(int a, string str1, string str2)
                {
                    number = a;
                    string1 = str1;
                    string2 = str2;
                }
            }

实施:

class Class1
    {
        [JsonProperty] 
        public int SomeInt{ get; private set;}
        [JsonProperty]
        [JsonConverter(typeof(SubClass1Converter))]
        public ISubClass1 SomeInterface { get; private set;}

        [JsonConstructor] 
        public Class1(int a, ISubClass1 subclass)
        {
            SomeInt= a;
            SomeInterface = subclass;
        }
    }

用法:

JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.Formatting = Formatting.Indented;
jsonSettings.Converters.Add(new SubClass1Converter()); //Optional
jsonSettings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor; //Optional

string jsonStr = JsonConvert.SerializeObject(cls1, jsonSettings);

Class1 deserializedObject = JsonConvert.DeserializeObject<Class1>(jsonStr , jsonSettings);

参考文献:

  • Casting interfaces for deserialization in JSON.NET