Как сериализовать объект исключения в C #? - PullRequest
70 голосов
/ 28 января 2009

Я пытаюсь сериализовать объект Exception в C #. Однако оказывается, что это невозможно, поскольку класс Exception не помечен как [Serializable]. Есть ли способ обойти это?

Если во время выполнения приложения что-то пойдет не так, я хочу получить информацию с исключением, которое произошло.

Мой первый рефлекс - это сериализация.

Ответы [ 10 ]

48 голосов
/ 28 января 2009

Создайте пользовательский класс исключений с атрибутом [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) { }
}
39 голосов
/ 28 января 2009

То, что я сделал раньше, - это создание собственного класса ошибок. Он инкапсулирует всю необходимую информацию об Исключении и может быть сериализуем в 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;
    }
}
37 голосов
/ 28 января 2009

Класс исключения имеет , помечен как сериализуемый и реализует ISerializable. См. MSDN: http://msdn.microsoft.com/en-us/library/system.exception.aspx

Если вы пытаетесь сериализовать в XML с использованием XmlSerializer, вы получите сообщение об ошибке для всех участников, которые реализуют IDictionary. Это ограничение XmlSerializer, но класс, безусловно, сериализуем.

17 голосов
/ 09 апреля 2010

mson пишет: «Я не уверен, почему вы захотите сериализовать исключение ...»

Я сериализирую исключения, чтобы передать исключение через веб-сервис вызывающему объекту, который может десериализовать, затем перебросить, зарегистрировать или обработать другим способом.

Я сделал это. Я просто создал Serializable класс-оболочку, который заменяет IDictionary на сериализуемую альтернативу (массив KeyValuePair)

/// <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
}
8 голосов
/ 28 января 2009

Если вы пытаетесь сериализовать исключение для журнала, может быть лучше сделать .ToString (), а затем сериализовать это в ваш журнал.

Но вот статья о том, как это сделать и почему. По сути, вам нужно реализовать ISerializable для вашего исключения. Если это системное исключение, я думаю, что они реализовали этот интерфейс. Если это чье-то исключение, вы можете создать его подкласс для реализации интерфейса ISerializable.

7 голосов
/ 15 марта 2011

На тот случай, если кто-то еще наткнется на этот поток (он находится на первой странице Google на сегодняшний день), это очень полезный класс для сериализации объекта 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;
        })())
    { ; }
}
5 голосов
/ 05 июня 2009

Создайте конструктор protected следующим образом (также вы должны отметить свой Exception класс [Serializable]):

protected MyException(System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context):base(info,context)
{
}
1 голос
/ 11 октября 2011

Это старая ветка, но достойна другого ответа.

@ mson удивлялся, почему кто-то захочет сериализовать исключение. Вот наша причина сделать это:

У нас есть приложение Prism / MVVM с представлениями в Silverlight и WPF с моделью данных в службах WCF. Мы хотим быть уверены, что доступ к данным и обновления происходят без ошибок. Если есть ошибка, мы хотим немедленно об этом узнать и сообщить пользователю, что что-то могло не получиться. В наших приложениях появится окно, информирующее пользователя о возможной ошибке. Фактическое исключение затем отправляется нам по электронной почте и сохраняется в SpiceWorks для отслеживания. Если ошибка возникает в службе WCF, мы хотим получить полное исключение обратно клиенту, чтобы этот процесс мог произойти.

Вот решение, которое я придумал, которое может быть использовано как клиентами WPF, так и Silverlight. Методы ниже a в «общей» библиотеке классов методов, используемых несколькими приложениями на каждом уровне.

Массив байтов легко сериализуется из службы WCF. Практически любой объект может быть преобразован в байтовый массив.

Я начал с двух простых методов, Object2Bytes и Bytes2Object. Они преобразуют любой объект в байтовый массив и обратно. 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. Если возникает ошибка, она передает исключение методу, который вызывает «ExceptionToByteArray», а затем генерирует уникальное целое число из настоящего времени. Это целое число используется в качестве ключа для кэширования массива байтов в течение 60 секунд. Служба WCF затем возвращает значение ключа клиенту.

Когда клиент видит, что получил целое число, отличное от 1, он вызывает метод GetException службы, используя это значение ключа. Служба извлекает байтовый массив из кэша и отправляет его обратно клиенту. Клиент вызывает «ByteArrayToException» и обрабатывает исключение, как я описал выше. 60 секунд - это достаточно времени для того, чтобы клиент запросил исключение из услуги. Менее чем за минуту, MemoryCache сервера очищается.

Я думаю, что это проще, чем создавать собственный класс Exception. Надеюсь, это кому-нибудь поможет позже.

1 голос
/ 28 января 2009

Я не уверен, почему вы хотите сериализовать исключение ...

Если бы я действительно хотел сделать то, что вы укажете, я бы создал собственный класс Exception, который реализует ISerializable. Вы можете сделать его дочерним по отношению к Исключению или сделать его полностью настраиваемым классом, который имеет и выполняет только то, что вам нужно.

0 голосов
/ 03 апреля 2019

[Serializable] Открытый класс CustomException: Exception { public CustomException: (Исключение исключения): база (исключение. Сообщение) {
Data.Add ( "StackTrace", exception.StackTrace); }
}

// Для сериализации вашего пользовательского исключения
JsonConvert.SerializeObject (customException);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...