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

标签 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 实现 ISerialized 的事实有关,但由于 Message 属性是虚拟的,我是否应该不允许像这样覆盖它并仍然让它工作?

是否有正确的方法来实现 Exception 并能够覆盖 Message 属性并使其仍然有效?

最佳答案

发生这种情况是正确的,因为 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 中。

因此,如果您希望消息属性的值显示在 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 字段的值不再是基类所需的值GrossException。但至少 JSON 很漂亮。

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

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

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

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

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

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)
    {
        base.GetObjectData(info, context);
        info.AddValue("OverriddenMessage", Message);
    }
}

这两种解决方案都可以避免代码异味。

关于c# - 使用 JSON.NET 自定义异常覆盖消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39984110/

相关文章:

c# - 欢迎消息在网络聊天中不可见,但在模拟器中有效

c# - 使用内置应用程序编写具有 facebook 共享或推文的 WP7 应用程序

asp.net - 自定义 ASP.NET 通用提供程序

asp.net - 有没有针对 asp.net 和 MongoDB 的托管解决方案?

php - 如何在 PHP 中使用带有 json_encode() 的瑞典字母?

python - 存储从Python脚本中执行的curl命令的内容

c# - 具有不同数据类型的 Linq

c# - 我可以在不验证的情况下加载 XmlDocument 吗?

javascript - 第二次打开时模态变成黑屏

java - 放心一点: Fail to convert a response to an array of objectsRe