Newtonsoft.Json中的自定义异常反序列化

3

我在使用Newtonsoft.Json 11.0.2版本中反序列化自定义异常时遇到了问题,在Newtonsoft.Json 10.0.3版本中正常工作。

我使用以下方法进行序列化和反序列化 -

result = JsonConvert.SerializeObject( <<object of type MyHttpException>> );
MyHttpException deserializedException = JsonConvert.DeserializeObject<MyHttpException>(result);

我在反序列化过程中遇到的错误是一个Newtonsoft.Json.JsonSerializationException

无法找到用于类型MyHttpException的构造函数。 一个类应该有一个默认构造函数、带参数的构造函数或带有JsonConstructor属性的构造函数。 路径 'HttpStatusCode',第2行,第19个位置。

如果我给MyHttpException和MyBaseException添加了一个无参构造函数,就不会出现任何异常。但内部异常没有被反序列化,为null。
我是否忽略了一些明显的东西?我不确定为什么这在10.0.3中可以工作,在11.0.2中却出问题了。
我的异常类 -
public sealed class MyHttpException : MyBaseException
{
    public MyHttpException(HttpStatusCode httpStatusCode, int MyStatusCode)
        : base(MyStatusCode) => HttpStatusCode = httpStatusCode;

    public MyHttpException(HttpStatusCode httpStatusCode, int MyStatusCode, string message)
        : base(MyStatusCode, message) => HttpStatusCode = httpStatusCode;

    public MyHttpException(HttpStatusCode httpStatusCode, int MyStatusCode, Exception innerException)
        : base(MyStatusCode, innerException) => HttpStatusCode = httpStatusCode;

    public MyHttpException(HttpStatusCode httpStatusCode, int MyStatusCode, string message, Exception innerException)
        : base(MyStatusCode, message, innerException) => HttpStatusCode = httpStatusCode;

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    private MyHttpException(SerializationInfo info, StreamingContext context)
        : base(info, context) => HttpStatusCode = (HttpStatusCode)info.GetValue("HttpStatusCode", typeof(HttpStatusCode));

    public HttpStatusCode HttpStatusCode { get; set; }

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
        {
            throw new ArgumentNullException("info");
        }

        info.AddValue("HttpStatusCode", HttpStatusCode);

        // MUST call through to the base class to let it save its own state
        base.GetObjectData(info, context);
    }
}

public abstract class MyBaseException : Exception
{
    public MyBaseException(int MyStatusCode) => this.MyStatusCode = MyStatusCode;

    public MyBaseException(int MyStatusCode, string message)
        : base(message) => this.MyStatusCode = MyStatusCode;

    public MyBaseException(int MyStatusCode, Exception innerException)
        : base("MyErrorCode: " + MyStatusCode + ". " + MyStatusCodes.GetDescription(MyStatusCode) + ". " + innerException.Message, innerException) => this.MyStatusCode = MyStatusCode;

    public MyBaseException(int MyStatusCode, string message, Exception innerException)
        : base(message, innerException) => this.MyStatusCode = MyStatusCode;

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    protected MyBaseException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        MyStatusCode = info.GetInt32("MyStatusCode");
    }

    public int MyStatusCode { get; set; }

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
        {
            throw new ArgumentNullException("info");
        }

        info.AddValue("MyStatusCode", MyStatusCode);

        // MUST call through to the base class to let it save its own state
        base.GetObjectData(info, context);
    }
}

谢谢

1个回答

4

在Json.NET 11中,对实现了ISerializable接口的类型进行了序列化方式的更改。根据发布说明:

  • 更改 - 实现了 ISerializable 接口但没有 [SerializableAttribute] 的类型不会使用 ISerializable 进行序列化

因此,您现在必须使用SerializableAttribute为您的异常类型进行标记:

[Serializable]
public sealed class MyHttpException : MyBaseException
{
}

[Serializable]
public abstract class MyBaseException : Exception
{
}

另外,您可以创建一个自定义合同解析器,以恢复旧的行为:

public class PreferISerializableContractResolver : DefaultContractResolver
{
    protected override JsonContract CreateContract(Type objectType)
    {
        var contract = base.CreateContract(objectType);

        if (!IgnoreSerializableInterface
            && contract is JsonObjectContract
            && typeof(ISerializable).IsAssignableFrom(objectType)
            && !objectType.GetCustomAttributes(true).OfType<JsonContainerAttribute>().Any())
        {
            contract = CreateISerializableContract(objectType);
        }

        return contract;
    }
}

您可能希望缓存合同解析器以获得最佳性能

为什么要进行这种更改?根据问题#1622:从System.Exception派生的类无法正确序列化/反序列化

Json.NET之前未正确序列化ISerializable类型。需要SerializableAttribute

有关更多信息,请参见此处dotnet/corefx#23415

进而,链接的问题dotnet/corefx问题#23415:尝试使用Newtonsoft.Json序列化DirectoryInfo实例时出现PlatformNotSupportedException表明该更改是在.NET Core团队的要求下进行的:

JamesNK 在 2017 年 8 月 29 日发表评论:

所以问题在于 Json.NET 检查类型是否实现了 ISerializable 接口,但没有检查 SerializableAttribute 属性?

ViktorHofer 在 2017 年 8 月 29 日发表评论:

没错 :)

因此,如果您使用 PreferISerializableContractResolver 而不是标记您的 ISerializable 类型为 [Serializable],您可能会在 .NET Core 中遇到这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接