鉴于该类型提供者 is not supported yet ,我需要一些其他方便的方法来解析 F# 中的 YAML 文件。但是,由于类型提供程序非常棒,当我在互联网上搜索替代解决方案时,我发现很难找到其他任何东西。
鉴于类型提供程序不在讨论范围内,在 F# 中解析配置文件的最简单方法是什么?
使用库很好,但是该库的接口(interface)越 OO,在 F# 中使用就越不方便。
我也不需要完整的“将任何 yaml 反序列化为给定类型/对象图”;像 xpath 查询之类的东西,但对于 YAML 来说非常好;我只是不想阅读流并手动解析它。
这是我当前的尝试,它在运行时失败,因为区分类型联合 OneOfSeveral
没有默认构造函数。它需要一些特殊处理并不让我感到惊讶,但我不知道如何去做。
open System
open YamlDotNet.Serialization
[<CLIMutable>]
type SpecificThing = {
foo : string
bar : int
}
type OneOfSeveral = Thing of SpecificThing
[<CLIMutable>]
type Root = {
option : OneOfSeveral
}
[<EntryPoint>]
let main argv =
let yaml = @"---
option:
thing:
foo: foobar
bar: 17
"
let deserializer = DeserializerBuilder().Build()
let config = deserializer.Deserialize<Root>(yaml)
printfn "%A" config
0
我也不确定我想如何在 YAML 的类型联合中表示选择;我考虑了几个选项:
# omit the 'option' level completely
thing:
foo: foobar
bar: 17
# have a specific field to discriminate on
option:
type: thing
foo: foobar
bar: 17
最后,对我来说,拥有一个灵活的配置对象图比拥有一个漂亮的 YAML 文件更重要,所以不管怎样……
最佳答案
是的,在让 F# 类型与 C# 目标库一起工作时,DU 缺少无参数构造函数是一个常见的痛点。
看 YamlDotNet,自定义序列化/反序列化似乎有两种方式,IYamlTypeConverter
和 IYamlConvertible
界面。
我已经简要介绍了如何编写通用 IYamlTypeConverter
对于所有联合类型,但由于无法将联合字段的序列化推迟回原始序列化程序而陷入困境。话虽如此,您可以实现 IYamlTypeConverters
专门针对您关心的类型。
另一个更轻量级的选择是为联合创建一个包装器类型,它带有一个实现 IYamlConvertible
的无参数构造函数。并在您的配置类型中公开它。
过去我采用了一种不同的方法——我使用的是 YamlDotNet 的表示模型部分,而不是序列化器/反序列化器接口(interface)。这是您从 yaml 字符串打开流的方式:
open System.IO
open YamlDotNet.RepresentationModel
let read yaml =
use reader = new StringReader(yaml)
let stream = YamlStream()
stream.Load(reader)
stream.Documents
let doc = read yaml
doc.[0].RootNode
这为您提供了文档的通用树表示。然后我会有一个沿着这些线的事件模式来简化遍历这棵树的编写函数:
let (|Mapping|Scalar|Sequence|) (yamlNode: YamlNode) =
match yamlNode.NodeType with
| YamlNodeType.Mapping ->
let node = yamlNode :?> YamlMappingNode
let mapping =
node.Children
|> Seq.map (fun kvp ->
let keyNode = kvp.Key :?> YamlScalarNode
keyNode.Value, kvp.Value)
|> Map.ofSeq
Mapping (node, mapping)
| YamlNodeType.Scalar ->
let node = yamlNode :?> YamlScalarNode
Scalar (node, node.Value)
| YamlNodeType.Sequence ->
let node = yamlNode :?> YamlSequenceNode
Sequence (node, List.ofSeq node.Children)
| YamlNodeType.Alias
| _ -> failwith "¯\_(ツ)_/¯"
现在您可以针对该表示编写函数,就像这里的 XPath 崇拜者:
let rec go (path: string list) (yamlNode: YamlNode) =
match path with
| [] -> Some yamlNode
| x::xs ->
match yamlNode with
| Mapping (n, mapping) ->
match mapping |> Map.tryFind x with
| Some nested ->
go xs nested
| None -> None
| Sequence _
| Scalar _ -> None
go ["option"; "thing"; "bar"] doc.[0].RootNode
关于parsing - 在 .NET Core 上用 F# 解析 YAML 的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46697298/