-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System.Text.Json - can't serialize exception #43026
Comments
.... first of all, you generally shouldn't return raw exceptions in an API. Not only because of potential security vulnerabilities, but also simply because there's going to be a bunch of implementation details which are in no way relevant to your callers (line numbers, full call stack). They should get logged, and some generic "we messed up" error returned (HTTP 500, maybe). If this is in reference to your caller doing something not supported by your API (calling I get a completely different error in dotnetfiddle, which might be its own problem... |
You're absolutely right. What was stated is absolutely right in broad sense. But I wouldn't like to share the whole picture related to our API in this narrow discussion. And also there's no need for people to give any recomendations when they don't have the whole picture and especially when the recomendation doesn't relate to the subject of the topic. |
It has been concluded in the past that (de)serialization of
@AlexeyKhrenov Does adding a converter for Exceptions solves your scenario? class ExceptionConverter : JsonConverter<Exception>
{
public override Exception Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, Exception value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("Message", value.Message);
// Add any other propoerties that you may want to include in your JSON.
// ...
writer.WriteEndObject();
}
} Then add such converter to your static void Main(string[] args)
{
var options = new JsonSerializerOptions();
options.Converters.Add(new ExceptionConverter());
try
{
throw new InvalidOperationException("New exception");
}
catch (Exception ex)
{
var str = JsonSerializer.Serialize(ex, options);
}
Console.WriteLine("Hello World!");
}
@Clockwork-Muse that error is because serialization of your |
Thank you, @jozkee ! Implementing a custom JsonConverter will definetely solve the issue. |
But @jozkee's code snippet only contains the part to write (serialize) exceptions, how to read (deserialize) them later? Did you implement that @AlexeyKhrenov ? |
Hi, @knocte ! No, unfortunately we switched to another lib |
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) NOTE: we had to use UTF-8 (instead of Encoding.Default) because otherwise the tests (committed from my macOS) would fail on Windows CI. Don't ask me why, I hate encodings too. [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) NOTE: we had to use ASCII (instead of Encoding.Default) because otherwise the tests (committed from my macOS) would fail on Windows CI (UTF-8 would fail too because of [3]0. Don't ask me why, I hate encodings too. [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) NOTE: we had to use Base64 because using any of the System.Text encodings (like Encoding.Default, ASCII, UTF-8 or UTF-7) would make the tests fail fail in weird ways (or only pass for certain OSs, e.g. see [3]). Don't ask me why, I hate encodings too. [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-default-encoding-based-string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-base64string format. (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
JSON deserialization of exceptions is extremely broken in JSON.NET [1] (and even in System.Text.Json [2]), so let's use a hybrid between binary serialization and JSON serialization: it will have a JSON part that hints about the most important human-readable details, but it will use a JSON element to marshall in binary-to-base64string format (I decided to use base64string after having many issues, e.g. [3], with UTF8 and other encodings). (I could have written a JsonConverter for this, but I was having trouble finding one out there that someone else had written already, then I decided to go for the simplest solution that I can write myself. I hate JSON type converters anyway.) [1] JamesNK/Newtonsoft.Json#801 [2] dotnet/runtime#43026 [3] https://stackoverflow.com/a/13674508/544947
@Clockwork-Muse This is an example from eShopOnContainers to return exception in the developer environment along with ProblemDetails. |
…` like `Exception` (because it is not supported as it poses security issues, confer to dotnet/runtime#41920 and dotnet/runtime#43026
Bump for this issue. I was not expecting to have to write a massive custom serializer to be able to log exceptions as JSON. If deserialization of |
Currently, .NET provides no way to deserialize exceptions without using the Recommendation is to use a custom converter as described in #43026 (comment) for exception serialization, however we recommend against writing converters that support exception deserialization. |
I just came across this yesterday / this morning. I ended up taking @jozkee 's partial answer and enhanced it by using reflection to get all properties of the Exception sans the public class ExceptionConverter<TExceptionType> : JsonConverter<TExceptionType>
{
public override bool CanConvert(Type typeToConvert)
{
return typeof(Exception).IsAssignableFrom(typeToConvert);
}
public override TExceptionType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException("Deserializing exceptions is not allowed");
}
public override void Write(Utf8JsonWriter writer, TExceptionType value, JsonSerializerOptions options)
{
var serializableProperties = value.GetType()
.GetProperties()
.Select(uu => new { uu.Name, Value = uu.GetValue(value) })
.Where(uu => uu.Name != nameof(Exception.TargetSite));
if (options?.IgnoreNullValues == true)
{
serializableProperties = serializableProperties.Where(uu => uu.Value != null);
}
var propList = serializableProperties.ToList();
if (propList.Count == 0)
{
// Nothing to write
return;
}
writer.WriteStartObject();
foreach (var prop in propList)
{
writer.WritePropertyName(prop.Name);
JsonSerializer.Serialize(writer, prop.Value, options);
}
writer.WriteEndObject();
}
} Used & tested like so: [Fact]
public void Serialize_NestedExceptions_ContainsNulls_Success()
{
var converter = new ExceptionConverter<Exception>();
var jsonOpts = new JsonSerializerOptions();
jsonOpts.Converters.Add(converter);
Exception ex;
try
{
throw new ArgumentOutOfRangeException("MyParamName", 42, "Some out of range error occured");
}
catch (Exception inny)
{
try
{
throw new Exception("I AM ERROR", inny);
}
catch (Exception outer)
{
ex = outer;
}
}
var json = JsonSerializer.Serialize(ex, jsonOpts);
Assert.Contains("42", json);
Assert.Contains("MyParamName", json);
Assert.Contains("I AM ERROR", json);
Assert.Contains("Some out of range error occured", json);
Assert.Contains("null", json);
} |
@bdriggs-axian note that you can avoid much of the reflection code by adding by adding |
Description
We're trying to use System.Text.Json for serialization and deserialization inside our API project with GraphQL. Sometimes the response object from GraphQL contains errors (exceptions). And serialization of this object fails. So I've come up with a simplified example for the problem:
The code above throws System.NotSupportedException: Serialization and deserialization of 'System.Type' instances are not supported
Expected
Exceptions are serialized
Workaround
Use Newtonsoft serializer in ASP.NET pipeline.
The text was updated successfully, but these errors were encountered: