JsonExceptionConverter
A converter to correctly serialize exception objects.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace NJsonSchema.Converters
{
public class JsonExceptionConverter : JsonConverter
{
private readonly DefaultContractResolver _defaultContractResolver = new DefaultContractResolver();
private readonly IDictionary<string, Assembly> _searchedNamespaces;
public override bool CanWrite => true;
public JsonExceptionConverter()
: this(new Dictionary<string, Assembly> {
{
typeof(JsonExceptionConverter).get_Name(),
typeof(JsonExceptionConverter).GetTypeInfo().get_Assembly()
}
})
{
}
public JsonExceptionConverter(IDictionary<string, Assembly> searchedNamespaces)
{
_searchedNamespaces = searchedNamespaces;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Exception ex = value as Exception;
if (ex != null) {
DefaultContractResolver defaultContractResolver = (serializer.ContractResolver as DefaultContractResolver) ?? _defaultContractResolver;
JObject jObject = new JObject();
jObject.Add(defaultContractResolver.GetResolvedPropertyName("discriminator"), ((object)ex).GetType().get_Name());
jObject.Add(defaultContractResolver.GetResolvedPropertyName("Message"), ex.Message);
jObject.Add(defaultContractResolver.GetResolvedPropertyName("StackTrace"), ex.StackTrace);
jObject.Add(defaultContractResolver.GetResolvedPropertyName("Source"), ex.Source);
jObject.Add(defaultContractResolver.GetResolvedPropertyName("InnerException"), (ex.InnerException != null) ? JToken.FromObject(ex.InnerException, serializer) : null);
foreach (KeyValuePair<PropertyInfo, string> exceptionProperty in GetExceptionProperties(value.GetType())) {
object value2 = exceptionProperty.Key.GetValue(ex);
if (value2 != null)
jObject.AddFirst(new JProperty(defaultContractResolver.GetResolvedPropertyName(exceptionProperty.Value), JToken.FromObject(value2, serializer)));
}
value = jObject;
}
serializer.Serialize(writer, value);
}
public override bool CanConvert(Type objectType)
{
return typeof(Exception).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = serializer.Deserialize<JObject>(reader);
if (jObject == null)
return null;
IContractResolver contractResolver = serializer.ContractResolver;
serializer.ContractResolver = (IContractResolver)Activator.CreateInstance(serializer.ContractResolver.GetType());
GetField(typeof(DefaultContractResolver), "_sharedCache").SetValue(serializer.ContractResolver, false);
dynamic val = serializer.ContractResolver as DefaultContractResolver;
if ((object)serializer.ContractResolver.GetType().GetRuntimeProperty("IgnoreSerializableAttribute") != null)
val.IgnoreSerializableAttribute = true;
if ((object)serializer.ContractResolver.GetType().GetRuntimeProperty("IgnoreSerializableInterface") != null)
val.IgnoreSerializableInterface = true;
if (jObject.TryGetValue("discriminator", StringComparison.OrdinalIgnoreCase, out JToken value)) {
string text = value.Value<string>();
if (!((object)objectType).GetType().get_Name().Equals(text)) {
Type type = Type.GetType("System." + text, false);
if ((object)type != null)
objectType = type;
else {
foreach (KeyValuePair<string, Assembly> searchedNamespace in _searchedNamespaces) {
type = searchedNamespace.Value.GetType(searchedNamespace.Key + "." + text);
if ((object)type != null) {
objectType = type;
break;
}
}
}
}
}
serializer.Converters.Remove(this);
object obj = jObject.ToObject(objectType, serializer);
serializer.Converters.Add(this);
foreach (KeyValuePair<PropertyInfo, string> exceptionProperty in GetExceptionProperties(obj.GetType())) {
object value2 = jObject.GetValue(val.GetResolvedPropertyName(exceptionProperty.Value))?.ToObject(exceptionProperty.Key.PropertyType);
if ((object)exceptionProperty.Key.SetMethod != null)
exceptionProperty.Key.SetValue(obj, value2);
else
GetField(objectType, "m_" + exceptionProperty.Value.Substring(0, 1).ToLowerInvariant() + exceptionProperty.Value.Substring(1))?.SetValue(obj, value2);
}
this.SetExceptionFieldValue(jObject, "Message", obj, "_message", val, serializer);
this.SetExceptionFieldValue(jObject, "StackTrace", obj, "_stackTraceString", val, serializer);
this.SetExceptionFieldValue(jObject, "Source", obj, "_source", val, serializer);
this.SetExceptionFieldValue(jObject, "InnerException", obj, "_innerException", val, serializer);
serializer.ContractResolver = contractResolver;
return obj;
}
private FieldInfo GetField(Type type, string fieldName)
{
FieldInfo declaredField = type.GetTypeInfo().GetDeclaredField(fieldName);
if ((object)declaredField == null && (object)type.GetTypeInfo().get_BaseType() != null)
return GetField(type.GetTypeInfo().get_BaseType(), fieldName);
return declaredField;
}
private IDictionary<PropertyInfo, string> GetExceptionProperties(Type exceptionType)
{
Dictionary<PropertyInfo, string> dictionary = new Dictionary<PropertyInfo, string>();
foreach (PropertyInfo item in from p in exceptionType.GetRuntimeProperties()
where p.GetMethod?.IsPublic ?? false
select p) {
JsonPropertyAttribute customAttribute = item.GetCustomAttribute<JsonPropertyAttribute>();
string value = (customAttribute != null) ? customAttribute.PropertyName : item.Name;
if (!new string[8] {
"Message",
"StackTrace",
"Source",
"InnerException",
"Data",
"TargetSite",
"HelpLink",
"HResult"
}.Contains(value))
dictionary[item] = value;
}
return dictionary;
}
private void SetExceptionFieldValue(JObject jObject, string propertyName, object value, string fieldName, DefaultContractResolver resolver, JsonSerializer serializer)
{
FieldInfo declaredField = typeof(Exception).GetTypeInfo().GetDeclaredField(fieldName);
object value2 = jObject[resolver.GetResolvedPropertyName(propertyName)].ToObject(declaredField.FieldType, serializer);
declaredField.SetValue(value, value2);
}
}
}