Skip to content

Commit

Permalink
Simplify generic serializer (WalletWasabi#13298)
Browse files Browse the repository at this point in the history
Instead of using static flags to prevent reentering into an infinite recursion, it is cleaner to remove the current converter from the list of converters.
  • Loading branch information
lontivero authored Jul 30, 2024
1 parent 88a6254 commit 301bbff
Showing 1 changed file with 15 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,30 @@

namespace WalletWasabi.WabiSabi.Models.Serialization;

public class GenericInterfaceJsonConverter<T> : JsonConverter<T>
public class GenericInterfaceJsonConverter<T>(IEnumerable<Type> types) : JsonConverter<T>
{
// This converter is a bit unusual because we need to add a new property to the
// serialized json string but the converter is called recursively and fails with
// a "Self referencing loop" exception.
// The workaround is detect it and prevent reentering by setting CanRead and
// CanWrite to false immediately after entering.
// see: https://github.com/JamesNK/Newtonsoft.Json/issues/386
[ThreadStatic]
private static bool IsReading;

[ThreadStatic]
private static bool IsWriting;

public GenericInterfaceJsonConverter(IEnumerable<Type> types)
{
Types = types;
}

public override bool CanWrite => !IsWriting;

public override bool CanRead => !IsReading;

public IEnumerable<Type> Types { get; }

public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer)
{
try
if (value is { } nonNullableValue)
{
IsWriting = true;
if (value is not null)
{
var stateTypeName = value.GetType().Name;
var jObject = (JObject)JToken.FromObject(value, serializer);
jObject.AddFirst(new JProperty("Type", stateTypeName));
jObject.WriteTo(writer);
}
}
finally
{
IsWriting = false;
var stateTypeName = nonNullableValue.GetType().Name;
var jObject = (JObject) JToken.FromObject(value, CreateJsonSerializer(serializer));
jObject.AddFirst(new JProperty("Type", stateTypeName));
jObject.WriteTo(writer);
}
}

public override T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
try
{
IsReading = true;
var jsonObject = JObject.Load(reader);
var typeName = jsonObject.Value<string>("Type");
var stateType = types.Single(t => t.Name == typeName);
return (T?)CreateJsonSerializer(serializer).Deserialize(jsonObject.CreateReader(), stateType);
}

var jsonObject = JObject.Load(reader);
var typeName = jsonObject.Value<string>("Type");
var stateType = Types.Single(t => t.Name == typeName);
return (T?)serializer.Deserialize(jsonObject.CreateReader(), stateType);
}
finally
private JsonSerializer CreateJsonSerializer(JsonSerializer serializer) =>
JsonSerializer.Create(new JsonSerializerSettings
{
IsReading = false;
}
}
Converters = serializer.Converters.Where(x => x is not GenericInterfaceJsonConverter<T>).ToArray()
});
}

0 comments on commit 301bbff

Please sign in to comment.