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

Deserializing custom exceptions in Newtonsoft.Json(反序列化 Newtonsoft.Json 中的自定义异常)

本文介绍了反序列化 Newtonsoft.Json 中的自定义异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Newtonsoft.Json 版本 11.0.2 中反序列化自定义异常时遇到问题.它在 Newtonsoft.Json 版本 10.0.3 中运行良好.

I've been having trouble deserializing custom exceptions in Newtonsoft.Json version 11.0.2. It works fine in Newtonsoft.Json version 10.0.3.

我序列化和反序列化使用 -

I serialize and deserialize using -

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

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

The error I get during deserialization is a Newtonsoft.Json.JsonSerializationException:

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

Unable to find a constructor to use for type MyHttpException. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'HttpStatusCode', line 2, position 19.

如果我向 MyHttpException 和 MyBaseException 添加无参数构造函数,我不会得到任何异常.但是 InnerException 没有反序列化,为空.

If I add a parameterless constructor to MyHttpException and MyBaseException, I don't get any exception. But the InnerException is not deserialized and is null.

我有什么明显的遗漏吗?我不确定为什么这会在 10.0.3 中工作并在 11.0.2 中中断.

Is there something obvious I'm missing? I'm not sure why this would work in 10.0.3 and break in 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);
    }
}

谢谢

推荐答案

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

In Json.NET 11 a change was made to how ISerializable types are serialized. According to the release notes:

  • 更改 - 实现 ISerializable 但没有 [SerializableAttribute] 的类型不使用 ISerializable 序列化

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

Thus you must now mark your exception types with SerializableAttribute:

[Serializable]
public sealed class MyHttpException : MyBaseException
{
}

[Serializable]
public abstract class MyBaseException : Exception
{
}

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

Alternatively, you could create a custom contract resolver that restores the old behavior:

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 的类不能正确序列化/反序列化:

Why was this change made? According to Issue #1622: classes deriving from System.Exception do not serialize/deserialize properly:

Json.NET 以前没有正确序列化 ISerializable 类型.SerializableAttribute 是必需的.

Json.NET previous wasn't serializing ISerializable types correctly. The SerializableAttribute is required.

查看此处了解更多信息dotnet/corefx#23415.

See here for more info dotnet/corefx#23415.

依次链接的问题 dotnet/corefx 问题 #23415:PlatformNotSupportedException 尝试序列化 DirectoryInfo 时带有 Newtonsoft.Json 的实例表明更改是应 .NET Core 团队的要求进行的:

And in turn the linked issue dotnet/corefx Issue #23415: PlatformNotSupportedException when attempting to serialize DirectoryInfo instance with Newtonsoft.Json indicates that the change was made at the request of the .NET Core team:

JamesNK 于 2017 年 8 月 29 日发表评论

JamesNK commented on Aug 29, 2017

所以问题是 Json.NET 正在检查一个类型是否实现了 ISerializable 但不检查 SerialiazableAttribute?

So the issue is Json.NET is checking that a type implements ISerializable but not also checking for the SerialiazableAttribute?

ViktorHofer 于 2017 年 8 月 29 日发表评论

ViktorHofer commented on Aug 29, 2017

正确:)

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

Thus if you do use PreferISerializableContractResolver instead of marking your ISerializable types with [Serializable], you might encounter this very issue in .NET Core.

这篇关于反序列化 Newtonsoft.Json 中的自定义异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:反序列化 Newtonsoft.Json 中的自定义异常