Skip to content

Commit 8156ea7

Browse files
authored
Create exception formatters for different exceptions (#4378)
This commit changes the ExceptionFormatterResolver to return a new instance of the now generic ExceptionFormatter<TException> for the type of exception. This fixes a bug where an ExceptionFormatter of type IJsonFormatter<Exception> is returned for derived exception types, resulting in an InvalidCastException at runtime. Formatter instances created are cached in ElasticsearchNetFormatterResolver for the type T, so will be reused once created. Fixes #4294
1 parent 6da641f commit 8156ea7

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

src/Elasticsearch.Net/Serialization/Formatters/ExceptionFormatter.cs

+10-8
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,32 @@
33
using System.Globalization;
44
using System.Reflection;
55
using System.Runtime.Serialization;
6+
using Elasticsearch.Net.Extensions;
67
using Elasticsearch.Net.Utf8Json;
78

89
namespace Elasticsearch.Net
910
{
1011
internal class ExceptionFormatterResolver : IJsonFormatterResolver
1112
{
12-
public static ExceptionFormatterResolver Instance = new ExceptionFormatterResolver();
13+
public static readonly ExceptionFormatterResolver Instance = new ExceptionFormatterResolver();
1314

1415
private ExceptionFormatterResolver() { }
1516

16-
private static readonly ExceptionFormatter ExceptionFormatter = new ExceptionFormatter();
17-
1817
public IJsonFormatter<T> GetFormatter<T>()
1918
{
2019
if (typeof(Exception).IsAssignableFrom(typeof(T)))
21-
return (IJsonFormatter<T>)ExceptionFormatter;
20+
{
21+
var type = typeof(ExceptionFormatter<>).MakeGenericType(typeof(T));
22+
return (IJsonFormatter<T>)type.CreateInstance();
23+
}
2224

2325
return null;
2426
}
2527
}
2628

27-
internal class ExceptionFormatter : IJsonFormatter<Exception>
29+
internal class ExceptionFormatter<TException> : IJsonFormatter<TException> where TException : Exception
2830
{
29-
private List<Dictionary<string, object>> FlattenExceptions(Exception e)
31+
private static List<Dictionary<string, object>> FlattenExceptions(Exception e)
3032
{
3133
var maxExceptions = 20;
3234
var exceptions = new List<Dictionary<string, object>>(maxExceptions);
@@ -100,7 +102,7 @@ private static void WriteStructuredExceptionMethod(Dictionary<string,object> o,
100102
o.Add("ExceptionMethod", exceptionMethod);
101103
}
102104

103-
public void Serialize(ref JsonWriter writer, Exception value, IJsonFormatterResolver formatterResolver)
105+
public void Serialize(ref JsonWriter writer, TException value, IJsonFormatterResolver formatterResolver)
104106
{
105107
if (value == null)
106108
{
@@ -134,7 +136,7 @@ public void Serialize(ref JsonWriter writer, Exception value, IJsonFormatterReso
134136
writer.WriteEndArray();
135137
}
136138

137-
public Exception Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) =>
139+
public TException Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) =>
138140
throw new NotSupportedException();
139141
}
140142
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Elastic.Xunit.XunitPlumbing;
5+
using Elasticsearch.Net;
6+
using FluentAssertions;
7+
8+
namespace Tests.Reproduce
9+
{
10+
public class GitHubIssue4294
11+
{
12+
[U] public void CanSerializeNullReferenceException()
13+
{
14+
var settings = new ConnectionConfiguration();
15+
16+
var dic = new Dictionary<string, object>()
17+
{
18+
["Exception"] = new NullReferenceException(),
19+
};
20+
21+
var data = PostData.Serializable(dic);
22+
using var ms = new MemoryStream();
23+
Action write = () => data.Write(ms, settings);
24+
25+
write.Should().NotThrow();
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)