如何在C#中序列化Exception对象?


74

我正在尝试在C#中序列化Exception对象。但是,由于Exception类未标记为,因此似乎是不可能的[Serializable]。有办法解决这个问题吗?

如果在应用程序执行过程中出了点问题,我想知道发生的异常。

我的第一反应是将其序列化。




System.Exception该类被标记为可序列化的msdn.microsoft.com/en-us/library/…–
Jodrell,

Answers:


41

我之前所做的是创建一个自定义的Error类。这封装了有关Exception的所有相关信息,并且可以XML序列化。

[Serializable]
public class Error
{
    public DateTime TimeStamp { get; set; }
    public string Message { get; set; }
    public string StackTrace { get; set; }

    public Error()
    {
        this.TimeStamp = DateTime.Now;
    }

    public Error(string Message) : this()
    {
        this.Message = Message;
    }

    public Error(System.Exception ex) : this(ex.Message)
    {
        this.StackTrace = ex.StackTrace;
    }

    public override string ToString()
    {
        return this.Message + this.StackTrace;
    }
}

1
请参阅“设计自定义例外” msdn.microsoft.com/zh-cn/library/ms229064(v=VS.100).aspx

5
这样做的问题是,如果有一个内部异常实际持有详细信息,则您将不包括该异常。
greektreat

51

使用[Serializable()]属性创建一个自定义Exception类。这是一个来自MSDN的示例:

[Serializable()]
public class InvalidDepartmentException : System.Exception
{
    public InvalidDepartmentException() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, System.Exception inner) : base(message, inner) { }

    // Constructor needed for serialization 
    // when exception propagates from a remoting server to the client.
    protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}

这使我遇到了异常和远程处理的问题。
0E322070

1
邮件内容呢?序列化构造函数是否应该实际执行一些操作以将消息从流上下文中拉出?
卢卡斯2012年

请注意,如果将它与未标记为Serializable的内部异常一起使用,则在序列化时会遇到运行时错误。如果内部异常来自.NET框架,这不是问题,但是如果您使用自定义异常类而忘记了Serializable属性,那么这将是一个失败点。
rybo103

2
System.Net.Sockets.SocketExceptionSystem.Net.Http.HttpRequestException 不是SERIALIZABLE
Kiquenet

39

Exception类标记为可序列化并实现ISerializable的。请参阅MSDN:http : //msdn.microsoft.com/en-us/library/system.exception.aspx

如果尝试使用序列化为XML XmlSerializer,则在实现的任何成员上都会遇到错误IDictionary。这是XmlSerializer的限制,但是该类肯定是可序列化的。


另一个限制:如果您使用Silverlight和WCF,则来回传递的对象的序列化将不能被ISerializable。我试图传递一个Exception对象,但是由于ISerializable在Silverlight中不可用,因此它无法正常工作。
约翰·吉尔默

17

mson写道:“我不确定为什么您要序列化异常...”

我对异常进行序列化,以通过Web服务将异常冒泡到可以反序列化,然后重新抛出,记录或以其他方式处理的调用对象。

我做到了 我只是创建了一个Serializable包装器类,用一个可序列化的替代项(KeyValuePair数组)替换IDictionary

/// <summary>
/// A wrapper class for serializing exceptions.
/// </summary>
[Serializable] [DesignerCategory( "code" )] [XmlType( AnonymousType = true, Namespace = "http://something" )] [XmlRootAttribute( Namespace = "http://something", IsNullable = false )] public class SerializableException
{
    #region Members
    private KeyValuePair<object, object>[] _Data; //This is the reason this class exists. Turning an IDictionary into a serializable object
    private string _HelpLink = string.Empty;
    private SerializableException _InnerException;
    private string _Message = string.Empty;
    private string _Source = string.Empty;
    private string _StackTrace = string.Empty;
    #endregion

    #region Constructors
    public SerializableException()
    {
    }

    public SerializableException( Exception exception ) : this()
    {
        setValues( exception );
    }
    #endregion

    #region Properties
    public string HelpLink { get { return _HelpLink; } set { _HelpLink = value; } }
    public string Message { get { return _Message; } set { _Message = value; } }
    public string Source { get { return _Source; } set { _Source = value; } }
    public string StackTrace { get { return _StackTrace; } set { _StackTrace = value; } }
    public SerializableException InnerException { get { return _InnerException; } set { _InnerException = value; } } // Allow null to be returned, so serialization doesn't cascade until an out of memory exception occurs
    public KeyValuePair<object, object>[] Data { get { return _Data ?? new KeyValuePair<object, object>[0]; } set { _Data = value; } }
    #endregion

    #region Private Methods
    private void setValues( Exception exception )
    {
        if ( null != exception )
        {
            _HelpLink = exception.HelpLink ?? string.Empty;
            _Message = exception.Message ?? string.Empty;
            _Source = exception.Source ?? string.Empty;
            _StackTrace = exception.StackTrace ?? string.Empty;
            setData( exception.Data );
            _InnerException = new SerializableException( exception.InnerException );
        }
    }

    private void setData( ICollection collection )
    {
        _Data = new KeyValuePair<object, object>[0];

        if ( null != collection )
            collection.CopyTo( _Data, 0 );
    }
    #endregion
}

3
Ctrl + K,Ctrl + D,然后您就可以停止抱怨换行了。
安东尼·布斯

4
@AdamNaylor查看(例如)属性。我发现最方便的是只需要查看6行代码即可查看所有属性,而不是(至少6x11 =)66行代码。您只是(就像我现在一样)在表达taste。因此,先Blurgh!说说自己的表达方式,然后再说正确的代码格式。但是我想你已经意识到了……
Mike de Klerk,2013年

VS自然会尝试抵制较长的代码行。口味不算在内。
Adam Naylor

我刚刚建议了一个替代方法,以便您的代码将支持一些不可序列化的异常(如SqlException)的序列化。非常感谢您分享您的代码。
Julio Nobre

出于好奇,如果 Data包含无法序列化的值怎么办?
佳能2015年

9

如果您尝试序列化日志的异常,最好执行.ToString(),然后将其序列化到日志。

但是这里有一篇关于如何做以及为什么的文章。基本上,您需要在异常上实现ISerializable。如果是系统异常,我相信他们已经实现了该接口。如果是其他人的例外,则可以将其子类化以实现ISerializable接口。


4
因此,差不多两年后的今天,我感到沮丧。介意解释一下,降票吗?
mmr 2010年

6
也许是因为文章链接损坏了?
thepirat000

8

这是一个非常有用的类,用于将Exception对象序列化为XElement(是,LINQ)对象:

http://seattlesoftware.wordpress.com/2008/08/22/serializing-exceptions-to-xml/

包含完整代码:

using System;
using System.Collections;
using System.Linq;
using System.Xml.Linq;
 
public class ExceptionXElement : XElement
{
    public ExceptionXElement(Exception exception)
        : this(exception, false)
    { ; }
 

    public ExceptionXElement(Exception exception, bool omitStackTrace)
        : base(new Func<XElement>(() =>
        {
            // Validate arguments
            if (exception == null)
            {
                throw new ArgumentNullException("exception");
            }
            
            // The root element is the Exception's type
            XElement root = new XElement(exception.GetType().ToString());
            if (exception.Message != null)
            {
                root.Add(new XElement("Message", exception.Message));
            }
            
            // StackTrace can be null, e.g.:
            // new ExceptionAsXml(new Exception())
            if (!omitStackTrace && exception.StackTrace != null)
            {
                vroot.Add(
                    new XElement("StackTrace",
                    from frame in exception.StackTrace.Split('\n')
                    let prettierFrame = frame.Substring(6).Trim()
                    select new XElement("Frame", prettierFrame))
                );
            }
            
            // Data is never null; it's empty if there is no data
            if (exception.Data.Count > 0)
            {
                root.Add(
                    new XElement("Data",
                        from entry in exception.Data.Cast<DictionaryEntry>()
                        let key = entry.Key.ToString()
                        let value = (entry.Value == null) ? "null" : entry.Value.ToString()
                        select new XElement(key, value))
                );
            }
            
            // Add the InnerException if it exists
            if (exception.InnerException != null)
            {
                root.Add(new ExceptionXElement(exception.InnerException, omitStackTrace));
            }
            return root;
        })())
    { ; }
}

5

创建一个protected这样的构造函数(也应该标记您的Exception[Serializable]):

protected MyException(System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context):base(info,context)
{
}

0

我不确定为什么要序列化异常...

如果确实要执行您指定的操作,则将创建一个实现ISerializable的自定义Exception类。您可以选择将其设为Exception的子级,也可以让它成为完全自定义的类,仅包含并满足您的需要。


0

这是一个旧线程,但是值得另一个答案。

@mson想知道为什么有人要序列化Exception。这是我们这样做的原因:

我们有一个Prism / MVVM应用程序,在Silverlight和WPF中都具有视图,在WCF服务中具有数据模型。我们希望确保数据访问和更新不会出错。如果有错误,我们想立即知道,并让用户知道可能发生了故障。我们的应用程序将弹出一个窗口,通知用户可能的错误。然后将实际的例外情况通过电子邮件发送给我们,并存储在SpiceWorks中以进行跟踪。如果错误发生在WCF服务上,我们想获取完整的异常返回给客户端,以便可以执行此过程。

这是我想出的解决方案,可以由WPF和Silverlight客户端处理。“通用”类库中位于下方的方法,该类库由每一层中的多个应用程序使用。

字节数组很容易从WCF服务进行序列化。几乎任何对象都可以转换为字节数组。

我从两个简单的方法开始,即Object2Bytes和Bytes2Object。这些将任何对象转换为Byte数组并返回。NetDataContractSerializer来自Windows版本的System.Runtime.Serialization命名空间。

Public Function Object2Bytes(ByVal value As Object) As Byte()
    Dim bytes As Byte()
    Using ms As New MemoryStream
        Dim ndcs As New NetDataContractSerializer()
        ndcs.Serialize(ms, value)
        bytes = ms.ToArray
    End Using
    Return bytes
End Function

Public Function Bytes2Object(ByVal bytes As Byte()) As Object
    Using ms As New MemoryStream(bytes)
        Dim ndcs As New NetDataContractSerializer
        Return ndcs.Deserialize(ms)
    End Using
End Function

最初,我们将所有结果作为一个对象返回。如果从服务返回的对象是字节数组,那么我们知道这是一个例外。然后,我们将调用“ Bytes2Object”并抛出异常进行处理。

这段代码的问题是,它与Silverlight不兼容。因此,对于我们的新应用程序,我保留了难以序列化对象的旧方法,并为异常创建了一对新方法。DataContractSerializer也来自System.Runtime.Serialization命名空间,但Windows和Silverlight版本中都存在。

Public Function ExceptionToByteArray(obj As Object) As Byte()
    If obj Is Nothing Then Return Nothing
    Using ms As New MemoryStream
        Dim dcs As New DataContractSerializer(GetType(Exception))
        dcs.WriteObject(ms, obj)
        Return ms.ToArray
    End Using
End Function

Public Function ByteArrayToException(bytes As Byte()) As Exception
    If bytes Is Nothing OrElse bytes.Length = 0 Then
        Return Nothing
    End If
    Using ms As New MemoryStream
        Dim dcs As New DataContractSerializer(GetType(Exception))
        ms.Write(bytes, 0, bytes.Length)
        Return CType(dcs.ReadObject(ms), Exception)
    End Using
End Function

如果没有错误发生,则WCF服务将返回1。如果发生错误,则将Exception传递给调用“ ExceptionToByteArray”的方法,然后从当前时间生成一个唯一的整数。它使用该整数作为键将字节数组缓存60秒。然后,WCF服务将键值返回给客户端。

当客户端看到返回的整数不是1时,它将使用该键值调用服务的“ GetException”方法。该服务从缓存中获取字节数组,并将其发送回客户端。客户端调用“ ByteArrayToException”并按照我上面的描述处理异常。60秒的时间足以让客户端从服务请求异常。在不到一分钟的时间内,将清除服务器的MemoryCache。

我认为这比创建自定义Exception类容易。我希望这对以后的人有所帮助。


1
这种实现方式的问题在于,客户端不一定加载与服务器相同的程序集,并且在尝试反序列化内部异常时,可能会引发SerializationException。
克里斯多夫(Christoph)

-2
[Serializable]
public class CustomException: Exception
{
    public CustomException: ( Exception exception ) : base(exception.Message)
    {             
        Data.Add("StackTrace",exception.StackTrace);
    }      
}

序列化自定义异常:

JsonConvert.SerializeObject(customException);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.