【问题标题】:Deserializing custom exceptions in Newtonsoft.Json反序列化 Newtonsoft.Json 中的自定义异常
【发布时间】:2021-09-16 11:45:44
【问题描述】:

我在 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 添加无参数构造函数,我不会得到任何异常。但是 InnerException 没有反序列化,为空。

我有什么明显的遗漏吗?我不确定为什么这会在 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);
    }
}

谢谢

【问题讨论】:

    标签: c# json.net deserialization


    【解决方案1】:

    在 Json.NET 11 中,对 ISerializable 类型的序列化方式进行了更改。根据release notes

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

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

    [Serializable]
    public sealed class MyHttpException : MyBaseException
    {
    }
    
    [Serializable]
    public abstract class MyBaseException : Exception
    {
    }
    

    或者,您可以创建一个custom contract resolver 来恢复旧行为:

    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;
        }
    }
    

    (您可能想cache the contract resolver for best performance。)

    为什么要进行此更改?根据Issue #1622: classes deriving from System.Exception do not serialize/deserialize properly

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

    查看这里了解更多信息dotnet/corefx#23415

    而链接的问题dotnet/corefx Issue #23415: PlatformNotSupportedException when attempting to serialize DirectoryInfo instance with Newtonsoft.Json 则表明更改是应 .NET Core 团队的要求进行的:

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

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

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

    正确:)

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-23
      • 2016-11-11
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      • 2014-12-27
      • 1970-01-01
      相关资源
      最近更新 更多