使用对象支持字段反序列化通用子类

De-Serializing generic sublass with object backing field

不确定如何正确地为这个问题命名,但这就是问题所在。

public sealed class ObjectPropertySubclassTest
{
    private sealed class CleverBaseClassConverter : JsonConverter<BaseClass>
    {
        public override bool CanWrite => false;

        public override void WriteJson(JsonWriter writer, BaseClass value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override BaseClass ReadJson(JsonReader reader, Type objectType, BaseClass existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var token = JToken.ReadFrom(reader);
            if (token["Components"] is JArray)
            {
                var collection=new CollectionClass();
                serializer.Populate(token.CreateReader(), collection);
                return collection;
            }
            else
            {
                if (token["Value"] is JArray)
                {
                    var obj = new SubClass<IEnumerable<BaseClass>>();
                    serializer.Populate(token.CreateReader(), obj);
                    return obj;
                }
                else
                {
                    var obj = new SubClass<object>();
                    serializer.Populate(token.CreateReader(), obj);
                    return obj;
                }
            }
        }
    }

    [JsonConverter(typeof(CleverBaseClassConverter))]
    private abstract class BaseClass
    {
        public object Value { get; set; }
    }

    private class SubClass<T>: BaseClass
    {
        public new T Value
        {
            get => (T) base.Value;
            set => base.Value = value;
        }
    }

    private sealed class CollectionClass : SubClass<IEnumerable<BaseClass>>
    {
        public IEnumerable<BaseClass> Components { get=>Value; set=>Value=value; }
        public bool ShouldSerializeValue() => false;
    }

    [Test]
    public void Test()
    {
        var item=new CollectionClass
        {
            Components=new BaseClass[] {new SubClass<string>{Value="hi"},new SubClass<int>{Value=5},   }
        };
        var json = JsonConvert.SerializeObject(item);

        var copy = JsonConvert.DeserializeObject<CollectionClass>(json);

        //why does copy.components have 4 items (2 doubling up)?
        //why does copy.value have 4 items (2 doubling up) as well?
    }
}

Serializaiton 按预期工作,但是当我将 json 反序列化为集合 class 时,它最终有 4 个项目而不是 2 个(在组件中)。我在反序列化方面做了一些根本性的错误吗? 还有为什么它仍然序列化 "Value" 以收集 class

JSON:(我有 json 转换器反序列化正确的子 class)

{"Components":[{"Value":"hi"},{"Value":5}],"Value":[{"Value":"hi"},{"Value":5}]}

使用

TypeNameHandling.Auto

而不是

TypeNameHandling.Object

我想出了如何让它工作以及为什么它会重复项目

private class SubClass<T>: BaseClass
{
    public new T Value
    {
        get => (T) base.Value;
        set => base.Value = value;
    }

    //Added this here in the generic subclass
    public virtual bool ShouldSerializeValue() => true;
}

然后在集合subsclass

private sealed class CollectionClass : SubClass<IEnumerable<BaseClass>>
{
    public IEnumerable<BaseClass> Components { get=>Value; set=>Value=value; }

    //this prevents value to be serialized for this type of class
    public override bool ShouldSerializeValue() => false;
}

最后为什么要复制它? 因为值是序列化的,并且它在反序列化值部分之后聚合 "components",所以在反序列化值之后,它将反序列化以已经反序列化的值列表为基础的组件,并将组件添加到该集合中。因此在收集 class 的情况下停止值序列化它不会有 "values" 的基础 json 因此不会复制它。