簡體   English   中英

如何在 C# 中序列化異常對象?

[英]How to serialize an Exception object in C#?

我正在嘗試在 C# 中序列化一個 Exception 對象。 但是,這似乎是不可能的,因為 Exception 類沒有標記為[Serializable] 有沒有辦法解決這個問題?

如果在應用程序執行過程中出現問題,我想知道發生的異常。

我的第一反應是序列化它。

使用[Serializable()]屬性創建自定義異常類。 這是從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) { }
}

我之前所做的是創建一個自定義的 Error 類。 這封裝了關於異常的所有相關信息,並且是 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;
    }
}

Exception 類標記為 Serializable 並實現 ISerializable。 請參閱 MSDN: http : //msdn.microsoft.com/en-us/library/system.exception.aspx

如果您嘗試使用XmlSerializer序列化為 XML,您將在實現IDictionary任何成員上遇到錯誤。 這是 XmlSerializer 的一個限制,但該類肯定是可序列化的。

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
}

如果您嘗試序列化日志的異常,最好執行 .ToString(),然后將其序列化到您的日志中。

這里有一篇關於如何做到這一點以及為什么這樣做的文章。 基本上,您需要在異常上實現 ISerializable。 如果是系統異常,我相信他們已經實現了該接口。 如果是其他人的異常,您也許可以將其子類化以實現 ISerializable 接口。

這是一個非常有用的類,用於將Exception對象序列化為XElement (yay, 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;
        })())
    { ; }
}

創建一個像這樣的protected構造函數(你也應該標記你的Exception[Serializable] ):

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

我遇到了一個類似的問題,我需要記錄在我的應用程序中拋出的Exception的詳細信息,但是,它包含一個不可序列化的成員,因此無法序列化。

作為一種解決方法,在Exception上調用.ToString()方法,該方法返回一個包含Exception詳細信息的string

string bar;

if (foo is Exception exception)
{
   bar = exception.ToString();
}

來源: Exception.ToString方法

這是一個舊線程,但值得另一個答案。

@mson 想知道為什么有人想要序列化一個異常。 這是我們這樣做的原因:

我們有一個 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 類更容易。 我希望這對以后的人有所幫助。

我不確定您為什么要序列化異常...

如果我確實想做您指定的操作,我將創建一個實現 ISerializable 的自定義 Exception 類。 您可以選擇讓它成為 Exception 的子類,或者您可以讓它成為一個完全自定義的類,它只具有並執行您需要的操作。

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

要序列化您的自定義異常:

JsonConvert.SerializeObject(customException);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM