为什么Json.NET试图反序列化我的get property属性?

时间:2018-03-21 16:22:02

标签: c# json serialization json.net

我一直在尝试序列化和反序列化我的对象,以便我能够指定某些属性要序列化但不反序列化。

示例代码如下:

public interface ISupYo
{
    string Hi { get; }
}
public class SupYo : ISupYo
{
    public string Hi { get; } = "heya";
}

public interface ISup
{
    int hiyo { get; }
}
public class Sup : ISup
{ 
    public Sup(int hiyo)
    {
        this.hiyo = hiyo;
    }

    public int hiyo { get; }
    public ISupYo yo => new SupYo();
}

var myNewSup = JsonConvert.SerializeObject(new Sup(2));
var mySup = JsonConvert.DeserializeObject<Sup>(myNewSup);

如果我从类Sup中删除构造函数,那么一切都很好。

但由于json.net尝试构建接口ISupYo,因此反序列化失败并出现以下错误...

Newtonsoft.Json.JsonSerializationException: 'Could not create an instance of type Scratchpad.Program+ISupYo. Type is an interface or abstract class and cannot be instantiated. Path 'yo.Hi', line 1, position 21.'

我在这里尝试了说明 Serialize Property, but Do Not Deserialize Property in Json.Net 但是反序列化也以同样的方式失败。

以这种方式使用JsonConverter http://pmichaels.net/tag/type-is-an-interface-or-abstract-class-and-cannot-be-instantiated/ 是成功的,因此在序列化/反序列化期间指定typeNameHandling和格式处理

为什么使用/不使用默认构造函数之间存在差异?

1 个答案:

答案 0 :(得分:2)

您看到的异常的原因是Json.NET功能的不幸交互:

  1. 如果被反序列化的对象具有只读引用类型成员,只要预先分配了JSON.NET,Json.NET就会populate其值的内容来自JSON流。即使成员的声明类型是抽象的或接口,也是如此,因为返回的真实对象显然必须是具体的。

    示例.Net小提示演示此here

  2. 如果反序列化的对象指定使用参数化构造函数,Json.NET将从JSON流中读取整个对象,将所有属性反序列化为其声明的类型,然后按名称将反序列化的属性与构造函数参数进行匹配( modulo case)并使用匹配的反序列化属性构造对象。最后,任何不匹配的属性都将被设置回对象。

  3. Json.NET是一个单通道解串器,它永远不会重新读取以前读过的JSON令牌。

  4. 可悲的是,前两个功能并不能很好地结合在一起。如果在构造类型之前必须反序列化参数化类型的所有属性,则不可能从JSON流填充预先分配的只读成员,因为已经读取了流。

    更糟糕的是,Json.NET似乎试图反序列化 not 对应于构造函数参数的JSON属性,但 do 对应于只读成员,即使它可能只是跳过它们。由于您的ISupYo yo成员是一个界面,因此您会看到所见的异常(除非您指定了TypeNameHandling,否则您不会)。这可能是一个错误;如果你愿意,你可以report an issue。具体问题似乎是JsonSerializerInternalReader.ResolvePropertyAndCreatorValues()缺少检查以查看非构造函数属性为Writable

    最简单的解决方法将需要使用特殊的JsonConverter,因为上述ResolvePropertyAndCreatorValues()会检查是否存在转换器。首先,介绍SkipDeserializationConverter

    public class SkipDeserializationConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            throw new NotImplementedException();
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            reader.Skip();
            return existingValue;
        }
    
        public override bool CanWrite { get { return false; } }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    并将其应用于您的类型,如下所示:

    [JsonConverter(typeof(SkipDeserializationConverter))]
    public ISupYo yo { get { return new SupYo(); } }
    

    转换器只是跳过当前正在读取的令牌的所有子节点而不尝试反序列化任何内容。使用它可能比使用TypeNameHandling更可取,因为后者会引入安全风险,如 TypeNameHandling caution in Newtonsoft Json 中所述。

    示例工作.Net fiddle