【问题标题】:Why is Json.NET trying to deserialise my get only property?为什么 Json.NET 试图反序列化我的 get only 属性?
【发布时间】:2018-08-30 21:14:15
【问题描述】:

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

示例代码如下:

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 和格式处理也是如此

为什么使用/不使用默认构造函数之间会出现这种差异?

【问题讨论】:

  • 你的序列化码是多少?究竟是什么失败了?
  • Sup中也需要添加无参数构造函数
  • 请提供带有错误消息的minimal reproducible example
  • @EhsanSajjad 这意味着如果我以无参数构造函数为目标,我将无法设置自动属性 ​​Sup.hiyo
  • @dbc 我不太确定我是否遵循,我习惯于拥有多个构造函数,使用 [JsonConstructor] 属性并以这种方式设置自动属性,这就是一个构造函数会发生的情况在这个例子中。但是我不明白为什么 ISupYo 类型的属性 yo 被拖入混合中,然后导致异常(尽管我知道这与它首先被序列化的事实有关)

标签: c# json serialization json.net


【解决方案1】:

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

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

    演示此here 的示例.Net fiddle。

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

  3. Json.NET 是一个单通道反序列化程序,它永远不会返回重新读取以前读取的 JSON 令牌。

遗憾的是,前两个功能不能很好地配合使用。如果在构造类型之前必须反序列化参数化类型的所有属性,则不可能从 JSON 流中填充预分配的只读成员,因为该流已被读取。

更糟糕的是,Json.NET 似乎试图反序列化 对应于构造函数参数但 对应于只读成员的 JSON 属性,即使它可能应该跳过它们。由于您的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

【讨论】:

  • 非常感谢,完全解释了我的问题!特别对待看到你升级你以前的不反序列化属性:)
猜你喜欢
  • 2023-03-02
  • 1970-01-01
  • 1970-01-01
  • 2021-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2014-08-22
相关资源
最近更新 更多