如何在 C# 中序列化异常对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/486460/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to serialize an Exception object in C#?
提问by Martin
I am trying to serialize an Exception object in C#. However, it appears that it is impossible since the Exception class is not marked as [Serializable]
. Is there a way to work around that?
我正在尝试在 C# 中序列化一个 Exception 对象。但是,这似乎是不可能的,因为 Exception 类没有标记为[Serializable]
。有没有办法解决这个问题?
If something goes wrong during the execution of the application, I want to be informed with the exception that occurred.
如果在应用程序执行过程中出现问题,我想知道发生的异常。
My first reflex is to serialize it.
我的第一反应是序列化它。
采纳答案by davogones
What I've done before is create a custom Error class. This encapsulates all the relevant information about an Exception and is XML serializable.
我之前所做的是创建一个自定义的 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;
}
}
回答by mson
I'm not sure why you would want to serialize the exception...
我不确定您为什么要序列化异常...
If I did want to do what you specify, I'd create a custom Exception class that implements ISerializable. You can choose to make it child of Exception or you could have it be a completely custom class that only has and does what you need.
如果我确实想做您指定的操作,我将创建一个实现 ISerializable 的自定义 Exception 类。您可以选择让它成为 Exception 的子类,或者您可以让它成为一个完全自定义的类,它只具有并执行您需要的操作。
回答by mmr
If you're trying to serialize the exception for a log, it might be better to do a .ToString(), and then serialize that to your log.
如果您尝试序列化日志的异常,最好执行 .ToString(),然后将其序列化到您的日志中。
But here'san article about how to do it, and why. Basically, you need to implement ISerializable on your exception. If it's a system exception, I believe they have that interface implemented. If it's someone else's exception, you might be able to subclass it to implement the ISerializable interface.
但这里有一篇关于如何做到这一点以及为什么这样做的文章。基本上,您需要在异常上实现 ISerializable。如果是系统异常,我相信他们已经实现了该接口。如果是其他人的异常,您也许可以将其子类化以实现 ISerializable 接口。
回答by David Crow
Create a custom Exception class with the [Serializable()]attribute. Here's an example taken from the MSDN:
使用[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) { }
}
回答by Rex M
The Exception class ismarked as Serializable and implements ISerializable.See MSDN: http://msdn.microsoft.com/en-us/library/system.exception.aspx
Exception 类被标记为 Serializable 并实现 ISerializable。请参阅 MSDN:http: //msdn.microsoft.com/en-us/library/system.exception.aspx
If you are attempting to serialize to XML using the XmlSerializer
, you will hit an error on any members that implement IDictionary
. That is a limitation of the XmlSerializer, but the class is certainly serializable.
如果您尝试使用 序列化为 XML XmlSerializer
,您将在任何实现IDictionary
. 这是 XmlSerializer 的一个限制,但该类肯定是可序列化的。
回答by davogones
Create a protected
constructor like this (also you should mark your Exception
class [Serializable]
):
创建一个protected
像这样的构造函数(你也应该标记你的Exception
类[Serializable]
):
protected MyException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context):base(info,context)
{
}
回答by Antony Booth
mson wrote: "I'm not sure why you would want to serialize the exception..."
mson 写道:“我不知道你为什么要序列化异常......”
I serialize exceptions to bubble up the exception, through a web service, to the calling object that can deserialize, then rethrow, log or otherwise handle it.
我将异常序列化以通过 Web 服务将异常冒泡到可以反序列化的调用对象,然后重新抛出、记录或以其他方式处理它。
I did this. I simply created a Serializable wrapper class that replaces the IDictionary with a serializable alternative (KeyValuePair array)
我这样做了。我只是创建了一个 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
}
回答by Tieson T.
Just in case someone else stumbles onto this thread (it's on page one of Google as of today), this is a very useful class for serializing an Exception
object into an XElement
(yay, LINQ) object:
以防万一其他人偶然发现此线程(截至今天,它位于 Google 的第一页),这是一个非常有用的类,用于将Exception
对象序列化为XElement
(是的,LINQ)对象:
http://seattlesoftware.wordpress.com/2008/08/22/serializing-exceptions-to-xml/
http://seattlesoftware.wordpress.com/2008/08/22/serializing-exceptions-to-xml/
Code included for completeness:
为了完整性而包含的代码:
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;
})())
{ ; }
}
回答by D'Hag
This is an old thread, but worthy of another answer.
这是一个旧线程,但值得另一个答案。
@mson wondered why anyone would want to serialize an Exception. Here's our reason for doing it:
@mson 想知道为什么有人想要序列化一个异常。这是我们这样做的原因:
We have a Prism/MVVM application with views in both Silverlight and WPF, with the Data Model in WCF services. We want to be sure that data access and updates occur without error. If there is an error, we want to know about it immediately, and let the user know that something may have failed. Our applications will pop a window informing the user of a possible error. The actual exception is then e-mailed to us, and stored in SpiceWorks for tracking. If the error occurs on a WCF service, we want to get the full exceptionback to the client so this process can happen.
我们有一个 Prism/MVVM 应用程序,其中包含 Silverlight 和 WPF 中的视图,以及 WCF 服务中的数据模型。我们希望确保数据访问和更新不会出错。如果有错误,我们想立即知道它,并让用户知道某些事情可能失败了。我们的应用程序将弹出一个窗口,通知用户可能出现的错误。然后将实际异常通过电子邮件发送给我们,并存储在 SpiceWorks 中以进行跟踪。如果错误发生在 WCF 服务上,我们希望将完整的异常返回给客户端,以便此过程可以发生。
Here is the solution I came up with that can be handled by both WPF and Silverlight clients. The methods below a in a "Common" class library of methods used by multiple applications in every layer.
这是我想出的解决方案,可以由 WPF 和 Silverlight 客户端处理。下面的方法位于每个层中多个应用程序使用的方法的“通用”类库中。
A byte array is easily serialized from a WCF service. Pretty much any object can be converted into a byte array.
字节数组很容易从 WCF 服务序列化。几乎任何对象都可以转换为字节数组。
I started with two simple methods, Object2Bytes and Bytes2Object. These convert any object to a Byte array and back. NetDataContractSerializer is from the Windows version of the System.Runtime.Serialization Namespace.
我从两个简单的方法开始,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
Originally, we would return all results as an Object. If the object coming back from the service was a byte array, then we knew it was an exception. Then we would call "Bytes2Object" and throw the exception for handling.
最初,我们会将所有结果作为对象返回。如果从服务返回的对象是一个字节数组,那么我们就知道这是一个异常。然后我们将调用“Bytes2Object”并抛出异常进行处理。
Trouble with this code is, it's incompatible with Silverlight. So for our new applications I kept the old methods for hard-to-serialize objects, and created a pair of new methods just for exceptions. DataContractSerializer is also from the System.Runtime.Serialization Namespace, but it is present in both the Windows and Silverlight versions.
这段代码的问题是,它与 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
When no errors occur, the WCF service returns 1. If an error occurs, it passes the Exception to a method that calls "ExceptionToByteArray," then generates a unique integer from the present time. It uses that integer as a key to cache the byte array for 60 seconds. The WCF service then returns the key value to the client.
当没有错误发生时,WCF 服务返回 1。如果发生错误,它会将异常传递给调用“ExceptionToByteArray”的方法,然后从当前时间生成一个唯一的整数。它使用该整数作为键来缓存字节数组 60 秒。WCF 服务然后将键值返回给客户端。
When the client sees that it got back an integer other than 1, it makes a call to the Service's "GetException" method using that key value. The service fetches the byte array from cache and sends it back to the client. The client calls "ByteArrayToException" and processes the exception as I described above. 60 seconds is plenty of time for the client to request the exception from the service. In less than a minute, the server's MemoryCache is cleared.
当客户端看到它返回一个不是 1 的整数时,它会使用该键值调用服务的“GetException”方法。该服务从缓存中获取字节数组并将其发送回客户端。客户端调用“ByteArrayToException”并按照我上面的描述处理异常。60 秒足以让客户端从服务请求异常。不到一分钟,服务器的MemoryCache 被清除。
I think this is easier than creating a custom Exception class. I hope this is a help to somebody later on.
我认为这比创建自定义 Exception 类更容易。我希望这对以后的人有所帮助。
回答by Guru
[Serializable]
public class CustomException: Exception
{
public CustomException: ( Exception exception ) : base(exception.Message)
{
Data.Add("StackTrace",exception.StackTrace);
}
}
To serialize your custom exception:
要序列化您的自定义异常:
JsonConvert.SerializeObject(customException);