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

时间:2018-06-13 19:55:37

标签: c# json.net deserialization

我在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&#39; HttpStatusCode&#39;,第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 个答案:

答案 0 :(得分:4)

在Json.NET 11中,对ISerializable类型的序列化方式进行了更改。根据{{​​3}}:

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

因此,您现在必须使用release notes标记您的例外类型:

[Serializable]
public sealed class MyHttpException : MyBaseException
{
}

[Serializable]
public abstract class MyBaseException : Exception
{
}

或者,您可以创建一个恢复旧行为的SerializableAttribute

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

(您可能需要custom contract resolver。)

为什么要做出这种改变?根据{{​​3}}:

  

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

     

有关详细信息,请参阅此处cache the contract resolver for best performance

反过来,链接问题Issue #1622: classes deriving from System.Exception do not serialize/deserialize properly表明更改是应.NET Core团队的要求进行的:

  

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

     

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

     

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

     

正确:)

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