F#, Json, WebAPI сериализация типов опций
Я использую типы записей в проекте F#, который я предоставляю проекту C# WebApi. Например:
type Account = {Amount:float; Number:int; Holder:string}
Основываясь на этом посте и этом посте, json сериализуется правильно.
{"Amount":100.0,"Number":1,"Holder":"Homer"}
Однако, когда я добавляю в запись тип опции,
type Account = {Amount:float; Number:int; Holder:string option }
Json расклеивается.
{"Amount":100.0,"Number":1,"Holder":{"Case":"Some","Fields":["Homer"]}}
Я бы хотел, чтобы json выглядел так же, как запись типа non-option с сериализатором, достаточно умным, чтобы взять значения и поместить их в/из тип опции автоматически.
Кто-нибудь создал пользовательский форматер для этой цели? Есть ли что-то такое, чего мне не хватает?
Спасибо
2 ответа:
Обычай Json.NET конвертер, который обрабатывает типы опционов и однотипные дискриминируемые союзы, действительно существует (или, по крайней мере, утверждает, что я только проверил случай типа опционов). Его можно найти здесь .
Использование:
let act = {Amount= 100.0; Number= 1; Holder= Some "Homer"} let json = JsonConvert.SerializeObject(act, new IdiomaticDuConverter())
Я попробовал конвертер, связанный в другом ответе, и мне не понравился вывод других DUs, которые не были опцией. Вместо этого вы можете выбрать только изменение поведения для типа опции, а не для всех DUs.
Я нашел этот конвертер, который изменит только поведение для типа option, чтобы сделать null на опции None, иначе значение. Исходный код / сведения об авторе можно найти здесь.
open System open Microsoft.FSharp.Reflection open Newtonsoft.Json open Newtonsoft.Json.Converters type OptionConverter() = inherit JsonConverter() override x.CanConvert(t) = t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>> override x.WriteJson(writer, value, serializer) = let value = if value = null then null else let _,fields = FSharpValue.GetUnionFields(value, value.GetType()) fields.[0] serializer.Serialize(writer, value) override x.ReadJson(reader, t, existingValue, serializer) = let innerType = t.GetGenericArguments().[0] let innerType = if innerType.IsValueType then (typedefof<Nullable<_>>).MakeGenericType([|innerType|]) else innerType let value = serializer.Deserialize(reader, innerType) let cases = FSharpType.GetUnionCases(t) if value = null then FSharpValue.MakeUnion(cases.[0], [||]) else FSharpValue.MakeUnion(cases.[1], [|value|])
Использование конвертера такое же, как и других ответ:
let json = JsonConvert.SerializeObject(myObj, new OptionConverter())