首页 文章

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

提问于
浏览
2

我在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属性标记的构造函数 . Path'HttpStatusCode',第2行,第19位 .

如果我向MyHttpException和MyBaseException添加无参数构造函数,我不会得到任何异常 . 但是InnerException没有反序列化并且为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 回答

  • 3

    在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中遇到此问题 .

相关问题