c# - 为什么 Json.NET 试图反序列化我的 get only 属性?

标签 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 试图构建接口(interface) 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和格式处理也是如此

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

最佳答案

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

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

    示例 .Net fiddle 演示此 here .

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

  3. Json.NET 是一个单 channel 反序列化器,永远不会返回重新读取之前读取的 JSON token 。

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

更糟糕的是,Json.NET 似乎试图反序列化 对应构造函数参数但 对应只读成员的 JSON 属性,即使它可能应该跳过它们。由于您的 ISupYo yo 成员是一个接口(interface),您会看到异常(除非您指定了 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(); } }

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

sample 加工 .Net fiddle .

关于c# - 为什么 Json.NET 试图反序列化我的 get only 属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49411617/

相关文章:

c# - Windows 窗体 C# 通过代码更改用户控件

c# - 遍历任意维度的数组

c# - 使用C#删除文件

ruby - 如何将xml文件转换为yaml文件?

c# - 使用 C# 在运行时合并两个对象的最佳方法是什么?

json - 在 Codable - Swift 中使用 "/"和 "%"

c++ - jsoncpp Json::LogicError,当我尝试解析一个对象时

javascript - 对 d3.js 树 JSON 使用 php SESSION 字符串而不是 .json 文件

Android - 用于持久存储的 Parcel

python - 需要对 Django REST Framework 中的序列化程序的 `Including extra context` 进行说明