-
-
Save AndreSteenveld/561fc3341e8ecd7c813b57824f5549da to your computer and use it in GitHub Desktop.
JsonConverter for LanguageExt.Option which serializes empty Options as nulls.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Linq; | |
using System.Reflection; | |
using LanguageExt; | |
using Newtonsoft.Json; | |
using Newtonsoft.Json.Linq; | |
public class OptionJsonConverter : JsonConverter | |
{ | |
private static readonly ConcurrentDictionary<Type, ReflectionTypeData> cachedReflection = | |
new ConcurrentDictionary<Type, ReflectionTypeData>(); | |
private ReflectionTypeData GetForOptionType(Type optionType) | |
{ | |
return cachedReflection.GetOrAdd(optionType.GetGenericArguments().First(), | |
t => new ReflectionTypeData(optionType)); | |
} | |
public override bool CanConvert(Type objectType) | |
{ | |
return objectType.IsGenericType && objectType.GetGenericTypeDefinition().IsAssignableFrom(typeof(Option<>)); | |
} | |
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |
{ | |
ReflectionTypeData typeData = GetForOptionType(value.GetType()); | |
if ((bool)typeData.IsNoneProp.GetValue(value)) | |
{ | |
writer.WriteNull(); | |
return; | |
} | |
serializer.Serialize(writer, typeData.IfNoneMethod.Invoke(value, new object[] { null })); | |
} | |
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, | |
JsonSerializer serializer) | |
{ | |
ReflectionTypeData typeData = GetForOptionType(objectType); | |
if (reader.TokenType == JsonToken.Null) | |
{ | |
return typeData.NoneField.GetValue(null); | |
} | |
object result = serializer.Deserialize(reader, objectType.GetGenericArguments().First()); | |
return typeData.SomeMethod.Invoke(null, new[] { result }); | |
} | |
private class ReflectionTypeData | |
{ | |
public ReflectionTypeData(Type optionType) | |
{ | |
IsNoneProp = optionType.GetProperty(nameof(Option<object>.IsNone), | |
BindingFlags.Instance | BindingFlags.Public); | |
IfNoneMethod = optionType.GetMethods(BindingFlags.Instance | BindingFlags.Public) | |
.Filter(m => m.Name == nameof(Option<object>.IfNone) | |
&& m.GetParameters().Length == 1 | |
&& !(m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0] | |
.ParameterType.GetGenericTypeDefinition().IsAssignableFrom(typeof(Func<>)))) | |
.First(); | |
NoneField = optionType.GetField(nameof(Option<object>.None), BindingFlags.Static | BindingFlags.Public); | |
SomeMethod = optionType.GetMethod(nameof(Option<object>.Some), | |
BindingFlags.Static | BindingFlags.Public); | |
} | |
public PropertyInfo IsNoneProp { get; } | |
public MethodInfo IfNoneMethod { get; } | |
public FieldInfo NoneField { get; } | |
public MethodInfo SomeMethod { get; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using LanguageExt; | |
using Newtonsoft.Json; | |
public class TestSer | |
{ | |
public Option<string> test1; | |
public Option<uint> test2; | |
public Option<List<double>> test3; | |
} | |
public class OptionJsonConverterTest | |
{ | |
static void Main(string[] args) | |
{ | |
JsonSerializer ser = new JsonSerializer(); | |
ser.Converters.Add(new OptionJsonConverter()); | |
var x = new TestSer | |
{ | |
test1 = Prelude.None, | |
test2 = Prelude.None, | |
test3 = Prelude.None, | |
}; | |
StringWriter sw = new StringWriter(); | |
ser.Serialize(sw, x); | |
string ss = sw.ToString(); | |
Console.WriteLine(ss); | |
TestSer y = ser.Deserialize<TestSer>(new JsonTextReader(new StringReader(ss))); | |
x = new TestSer | |
{ | |
test1 = Prelude.Some("123"), | |
test2 = Prelude.Some(123123u), | |
test3 = Prelude.Some(new List<double> { 1, 2, 3 }) | |
}; | |
sw = new StringWriter(); | |
ser.Serialize(sw, x); | |
ss = sw.ToString(); | |
Console.WriteLine(ss); | |
y = ser.Deserialize<TestSer>(new JsonTextReader(new StringReader(ss))); | |
Console.ReadLine(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment