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 8

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())