|
| 1 | +namespace FsCodec.SystemTextJson |
| 2 | + |
| 3 | +open System |
| 4 | +open System.Linq.Expressions |
| 5 | +open System.Text.Json |
| 6 | +open System.Text.Json.Serialization |
| 7 | + |
| 8 | +type RejectNullConverter<'T>() = |
| 9 | + inherit System.Text.Json.Serialization.JsonConverter<'T>() |
| 10 | + |
| 11 | + static let defaultConverter = JsonSerializerOptions.Default.GetConverter(typeof<'T>) :?> JsonConverter<'T> |
| 12 | + let msg () = sprintf "Expected value, got null. When rejectNull is true you must explicitly wrap optional %s values in an 'option'" typeof<'T>.Name |
| 13 | + |
| 14 | + override _.HandleNull = true |
| 15 | + |
| 16 | + override _.Read(reader, typeToConvert, options) = |
| 17 | + if reader.TokenType = JsonTokenType.Null then msg () |> nullArg else |
| 18 | + defaultConverter.Read(&reader, typeToConvert, options) |
| 19 | + // Pretty sure the above is the correct approach (and this unsurprisingly loops, blowing the stack) |
| 20 | + // JsonSerializer.Deserialize(&reader, typeToConvert, options) :?> 'T |
| 21 | + |
| 22 | + override _.Write(writer, value, options) = |
| 23 | + if value |> box |> isNull then msg () |> nullArg |
| 24 | + defaultConverter.Write(writer, value, options) |
| 25 | + // JsonSerializer.Serialize<'T>(writer, value, options) |
| 26 | + |
| 27 | +type RejectNullConverterFactory(predicate) = |
| 28 | + inherit JsonConverterFactory() |
| 29 | + new() = |
| 30 | + RejectNullConverterFactory(fun (t: Type) -> |
| 31 | + t.IsGenericType |
| 32 | + && let gtd = t.GetGenericTypeDefinition() in gtd = typedefof<Set<_>> || gtd = typedefof<list<_>>) |
| 33 | + override _.CanConvert(t: Type) = predicate t |
| 34 | + |
| 35 | + override _.CreateConverter(t, _options) = |
| 36 | + let openConverterType = typedefof<RejectNullConverter<_>> |
| 37 | + let constructor = openConverterType.MakeGenericType(t).GetConstructors() |> Array.head |
| 38 | + let newExpression = Expression.New(constructor) |
| 39 | + let lambda = Expression.Lambda(typeof<ConverterActivator>, newExpression) |
| 40 | + |
| 41 | + let activator = lambda.Compile() :?> ConverterActivator |
| 42 | + activator.Invoke() |
0 commit comments