使用JSON.NET自定义异常覆盖消息

时间:2016-10-11 18:23:36

标签: c# asp.net json serialization json.net

如果您创建了一个自定义异常,它会覆盖虚拟属性Message,如下所示:

public class GrossException : Exception
{
    public GrossException() : base("Eww, gross") { }
}

public class BarfException : GrossException
{
    public override string Message { get; } = "BARF!!";
}

然后,当通过ASP.NET使用JSON.NET发送BarfException时,Message属性将包含

  

“Eww,gross”

而不是被覆盖的值。我相信这与Exception实现ISerializable的事实有关,但由于Message属性是虚拟的,我是否应该允许这样覆盖它并仍然有效?

是否有正确的方法来实现Exception并且能够覆盖Message属性并仍然可以使用它?

1 个答案:

答案 0 :(得分:4)

这是正确的,因为Exception实现了ISerializable和Json.NET supports this interface。具体来说,在参考源中,在方法Exception.GetObjectData(SerializationInfo info, StreamingContext context)中,Exception类型序列化基础字段,而不是属性:

        info.AddValue("Message", _message, typeof(String));

Microsoft可能已经这样做了,因为如果未设置基础字段,Message属性具有默认值:

    public virtual String Message {
           get {  
            if (_message == null) {
                if (_className==null) {
                    _className = GetClassName();
                }
                return Environment.GetResourceString("Exception_WasThrown", _className);

            } else {
                return _message;
            }
        }
    }

通过序列化字段而不是属性,默认消息的异常将在反序列化时自动显示在接收系统的CurrentUICulture中的可见消息。

因此,如果您希望message属性的值显示在JSON中,而不是基础字段,则需要覆盖GetObjectData()。并且,如果您尝试添加与预先存在的值同名的值,AddValue()会引发异常,并且SerializationInfo没有SetValue()方法来替换当前值,那么我需要做一些代码味道的事情:

public class GrossException : Exception
{
    public GrossException() : base("Eww, gross") { }

    protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

public class BarfException : GrossException
{
    public BarfException() : base() { }

    protected BarfException(SerializationInfo info, StreamingContext context) : base(info, context) { }

    public override string Message { get { return "BARF!!"; } }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        var tempInfo = new SerializationInfo(GetType(), new FormatterConverter());
        base.GetObjectData(tempInfo, context);
        foreach (SerializationEntry entry in tempInfo)
        {
            if (entry.Name != "Message")
            {
                info.AddValue(entry.Name, entry.Value, entry.ObjectType);
            }
        }
        info.AddValue("Message", Message);
    }
}

正如您所看到的,此解决方案违反了继承层次结构的设计,因为基础_message字段的值不再是基类{{1}所需的值 }。但至少JSON很漂亮。

更好的解决方案是修改GrossException类型以使其具有可以指定消息的受保护构造函数:

GrossException

或者,如果您只是想在JSON中查看被覆盖的消息以进行调试(例如,因为您正在记录异常),您可以将其添加到序列化流中,如下所示:

public class GrossException : Exception
{
    public GrossException() : base("Eww, gross") { }

    protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }

    protected GrossException(string message) : base(message) { }
}

任何一种解决方案都可以避免代码异味。