将F#

几天前,我在F#中发布了一个关于用枚举反序列化的问题。 问题在这里:Deserialization in F# vs. C#

答案指向艾萨克·亚伯拉罕(Isaac Abraham)编写的某些代码,网址为:https://gist.github.com/isaacabraham/ba679f285bfd15d2f53e

但是我面临另一个问题:

如果要反序列化的对象的对象类型为'enum option',则反序列化将失败,而如果类型只是'enum',则反序列化将起作用。

一个最小的例子:

type TestType =
    | A = 0
    | B = 1

type TestObjectA =
      {
           test : TestType
      }

type TestObjectB =
     {
         test : TestType option
     }


let x = "{\"test\":\"A\"}"
let TestA = Deserialize<TestObjectA> x // will work
let TestB = Deserialize<TestObjectB> x // will fail

大型反序列化代码位于:https://pastebin.com/95JZLa6j

我把整个代码放在了一个小提琴中:https://dotnetfiddle.net/0Vc0Rh 但是它不能从那里运行,因为他们支持的F#版本将不接受'object'关键字。

所以,我的问题是:为什么我不能在枚举上使用选项类型,但是可以在其他类型上使用呢?附带说明一下,由于我对F#还是很陌生,所以尽管花了一些时间尝试对其进行故障排除,但是我并没有完全理解Isaac的代码。

我的理解是这一行:                 |>顺序图(有趣(值,propertyInfo)-> Convert.ChangeType(值,propertyInfo.PropertyType))

将尝试将类型转换为正确的枚举,而不转换为枚举选项。

作为一个额外的问题,是否存在一个可行的解决方案,可以对枚举进行完全惯用的反序列化? (不使用null类型)

adlofboy 回答:将F#

open System.IO

type TestType =
    | A = 0
    | B = 1

type TestObjectB =
     {
         test : TestType option
     }

let jsonSerializeToString obj = 
    use writer = new StringWriter()
    let ser =  new Newtonsoft.Json.JsonSerializer()
    ser.Formatting <- Newtonsoft.Json.Formatting.Indented
    ser.Serialize(writer,obj)
    writer.ToString()

let jsonDeserializeFromString str =
    Newtonsoft.Json.JsonConvert.DeserializeObject<TestObjectB>(str)

let Test obj = 
    let str = jsonSerializeToString obj
    let obj' = jsonDeserializeFromString str
    obj'

[<EntryPoint>]
let main argv =
    { test = Some TestType.B } |> Test |> ignore
    { test = None } |> Test |> ignore
    0

注意:如果需要序列化大量对象,则将它们流式传输到文件而不是内存中字符串,以避免OutOfMemoryException。像use writer = File.CreateText(filePath)

,
  

作为一个奖励问题,是否有一个可以解决问题的可行解决方案   枚举的惯用反序列化?

我在生产中使用了Microsoft.FsharpLu.Json包,发现它在“普通” javascript和惯用F#之间进行序列化和反序列化时效果很好。注意Microsoft.FsharpLu.Json依赖于引擎盖下的Newtonsoft.Json

下面是您的类型和测试字符串的示例,使用Expecto进行测试。

namespace FsharpLuJsonTest

open Newtonsoft.Json
open Microsoft.FSharpLu.Json
open Expecto
open Expecto.Flip

// Setup for FSharpLu.Json
type JsonSettings =
    static member settings =
        let s = JsonSerializerSettings(
                    NullValueHandling = NullValueHandling.Ignore,MissingMemberHandling = MissingMemberHandling.Ignore)
        s.Converters.Add(CompactUnionJsonConverter())
        s
    static member formatting = Formatting.None

type JsonSerializer = With<JsonSettings>

// Your example
type TestType =
    | A = 0
    | B = 1

type TestObjectA = { test : TestType }
type TestObjectB = { test : TestType option }

module Tests =

    let x = """{"test":"A"}"""

    [<Tests>]
    let tests =
        testList "Deserialization Tests" [
            testCase "To TestObjectA" <| fun _ ->
                JsonSerializer.deserialize x
                |> Expect.equal "" { TestObjectA.test = TestType.A }

            testCase "To TestObjectB" <| fun _ ->
                JsonSerializer.deserialize x
                |> Expect.equal "" { TestObjectB.test = Some TestType.A }
        ]

module Main =

    [<EntryPoint>]
    let main args =
        runTestsInAssembly defaultConfig args

如您所见,FsharpLu.Json以您喜欢的方式开箱即用地支持区分联合和选项类型。与Chiron之类的其他解决方案(允许更多自定义)相比,FsharpLu.Json的灵活性较差,但我倾向于使用FsharpLu.Json的自以为是的方法。


我还没有亲自使用过它,但是具有JsonUnionEncoding.ExternalTag设置的新FSharp.SystemText.Json库应该与FsharpLu.Json大致相同。该库在幕后使用Microsoft的新System.Text.Json库,而不是Newtonsoft.Json

本文链接:https://www.f2er.com/3127307.html

大家都在问