我需要将使用 F# 可区分联合表示的抽象语法树保留为人类可读的紧凑格式,例如 F# 语言中已用于构造可区分联合的格式,我可以稍后将其读回可区分联合实例。我有点惊讶 F# 库不支持这一点,因为它肯定必须在编译器和 F# 交互中以一种有效的方式完成。
是否有任何免费/开源实现以合理(但不一定非常)有效的方式执行此操作?
注意:我不想要基于 XML 的序列化。
我需要将使用 F# 可区分联合表示的抽象语法树保留为人类可读的紧凑格式,例如 F# 语言中已用于构造可区分联合的格式,我可以稍后将其读回可区分联合实例。我有点惊讶 F# 库不支持这一点,因为它肯定必须在编译器和 F# 交互中以一种有效的方式完成。
是否有任何免费/开源实现以合理(但不一定非常)有效的方式执行此操作?
注意:我不想要基于 XML 的序列化。
编辑:下面的答案都没有真正符合您的标准,但我发布以防其他寻找联合序列化的人发现它们有用。我不知道任何库方法来重新解析sprintf "%A"
联合上的输出 - 请记住,编译器和 FSI 有一个非常不同的任务,知道范围和处理名称空间和限定名称以及阴影等等,并且即使忽略这一点,解析联合携带的数据(整数、字符串、任意对象等)本身也可能是一项完整的任务。
这是联合序列化的一种策略,作为一个小示例程序的一部分。(KnownTypeAttribute
可以采用方法名称,并且您可以使用一些反射来获取类型。)这是向联合添加少量代码以获取序列化的一种非常简单的方法。
open Microsoft.FSharp.Reflection
open System.Reflection
open System.Runtime.Serialization
open System.Xml
[<KnownType("KnownTypes")>]
type Union21WithKnownTypes =
| Case1 of int * int
| Case2 of string
static member KnownTypes() =
typeof<Union21WithKnownTypes>.GetNestedTypes(
BindingFlags.Public
||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion
let dcs = new DataContractSerializer(typeof<Union21WithKnownTypes[]>)
let arr = [| Case1(1,1); Case2("2") |]
printfn "orig data: %A" arr
let sb = new System.Text.StringBuilder()
let xw = XmlWriter.Create(sb)
dcs.WriteObject(xw, arr)
xw.Close()
let s = sb.ToString()
printfn ""
printfn "encoded as: %s" s
printfn ""
let xr = XmlReader.Create(new System.IO.StringReader(s))
let o = dcs.ReadObject(xr)
printfn "final data: %A" o
这是 JSON 版本:
open Microsoft.FSharp.Reflection
open System.Reflection
open System.Runtime.Serialization
open System.Runtime.Serialization.Json
open System.Xml
[<KnownType("KnownTypes")>]
type Union21WithKnownTypes =
| Case1 of int * int
| Case2 of string
static member KnownTypes() =
typeof<Union21WithKnownTypes>.GetNestedTypes(
BindingFlags.Public
||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion
let dcs = new DataContractJsonSerializer(typeof<Union21WithKnownTypes[]>)
let arr = [| Case1(1,1); Case2("2") |]
printfn "orig data: %A" arr
let stream = new System.IO.MemoryStream()
dcs.WriteObject(stream, arr)
stream.Seek(0L, System.IO.SeekOrigin.Begin) |> ignore
let bytes = Array.create (int stream.Length) 0uy
stream.Read(bytes, 0, int stream.Length) |> ignore
let s = System.Text.Encoding.ASCII.GetString(bytes)
printfn ""
printfn "encoded as: %s" s
printfn ""
stream.Seek(0L, System.IO.SeekOrigin.Begin) |> ignore
let o = dcs.ReadObject(stream)
printfn "final data: %A" o