Null value received in a custom formatter for Exception
Opened this issue · 3 comments
Bug description
When trying to serialize/deserialize an exception object using TypelessContractlessStandardResolver, some details like StackTrace and Source are getting lost on the deserialized object.
Hence, I tried to write a custom IMessagePackFormatter. There are two scenarios now :
-
Serialize an exception with no inner exception
The value parameter received in Serialize method is always null
-
Serialize an exception with inner exception
The value parameter received in Serialize method is the inner exception and not the actual exception that was passed for serialization
As a result, I am not able to get a workaround even with a custom IMessagePackFormatter.
Repro steps
- Add a custom IMessagePackFormatter
internal class ExceptionFormatter : IMessagePackFormatter<Exception>
{
public Exception Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
}
public void Serialize(ref MessagePackWriter writer, Exception value, MessagePackSerializerOptions options)
{
/// value is null here if exception has no inner exception else inner exception instead of actual exception passed for serialization
}
}
- Configure a custom resolver to use the custom ExceptionFormatter
interface ISerializer
{
byte[] Serialize(object obj);
T Deserialize<T>(byte[] bytes);
}
public class MessagePackTypeLessSerializer : ISerializer
{
private static MessagePackSerializerOptions serializerOptions;
static MessagePackTypeLessSerializer()
{
var resolver = CompositeResolver.Create(
[new ExceptionFormatter()],
[TypelessContractlessStandardResolver.Instance]);
serializerOptions = MessagePackSerializerOptions.Standard.WithResolver(resolver); ;
}
public byte[] Serialize(object obj)
{
return MessagePackSerializer.Typeless.Serialize(obj, serializerOptions);
}
public T Deserialize<T>(byte[] bytes)
{
if (MessagePackSerializer.Typeless.Deserialize(bytes, serializerOptions) is T result)
{
return result;
}
throw new ArgumentException($"Deserialized object is not of type {typeof(T)}");
}
}
- Throw an exception and try to serialize
ISerializer serializer = new MessagePackContractLessSerializer();
var exception = GetException();
var exceptionSerialized = serializer.Serialize(exception);
Exception GetException()
{
try
{
throw new InvalidOperationException("Invalid Operation");
//throw new InvalidOperationException("Invalid Operation", new ArgumentException("Inner Exception"));
}
catch (Exception ex)
{
return ex;
}
}
Expected behavior
Correct exception object should be received in Serialize method of the IMessagePackFormatter that was passed for serialization.
Actual behavior
null/inner exception object is received in Serialize method of the IMessagePackFormatter instead of actual exception passed for serialization.
Version used:
.Net Core 8.0 , MessagePack 2.5.172 : Broken
.Net Framework 4.7, MessagePack 2.5.172 : Partly broken (When no inner exception, we get the correct value. Same as .net core when there is inner exception)
To set expectations, the contractless side is very much a your-mileage-may-vary sort of thing. Exception serialization is particularly non-trivial and unlikely to produce a good result. When the .NET team come up with a recommendation for how to properly and securely support exception serialization, I bet messagepack will pick it up.
In the meantime, if you're not getting what you want from contractless, you should follow the README to author your own resolver/formatter for them.
That's pretty weird. No idea why that would happen. I suggest you search the callstack to find which method is suppressing the value.
Or if you can't figure it out, paid support is an option.