什么是在.NET Core上解析F#中YAML的最佳方法?

时间:2017-10-11 20:49:03

标签: parsing f# yaml

鉴于类型提供程序is not supported yet,我需要一些其他方便的方法来解析F#中的YAML文件。然而,由于类型提供商非常棒,我在搜索互联网寻找替代解决方案时发现很难找到其他任何东西。

在F#中解析配置文件的最简单方法是什么,因为类型提供程序不在桌面上?

使用库很好,但是该库的接口越多,在F#中使用它就越不方便。

我不需要完整的#34;将任何yaml反序列化为给定的类型/对象图"要么;像xpath查询这样的东西,但对于YAML来说非常好;我只是不想阅读流并手动解析它。

这是我当前的尝试,它在运行时失败,因为区分的类型union 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文件更重要,所以无论如何......

2 个答案:

答案 0 :(得分:5)

是的,当使用F#类型与C#目标库一起工作时,DU缺少无参数构造函数是一个常见的难点。

看看YamlDotNet,似乎有两种方法可以自定义序列化/反序列化,IYamlTypeConverterIYamlConvertible接口。

我已经简要地介绍了为所有联合类型编写通用IYamlTypeConverter,但是因为无法将联合字段的序列化推迟回原始序列化程序而陷入困境。话虽如此,您可以专门针对您关注的类型实施IYamlTypeConverters

另一个更轻量级的选项是使用无参数构造函数为union创建一个包装器类型,该构造函数将实现IYamlConvertible并在您的配置类型中公开它。

我过去采用了不同的方法 - 我使用的是YamlDotNet的表示模型部分,而不是串行器/解串器接口。这是您从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

答案 1 :(得分:0)

为时已晚,但将来可能对其他人有用


已找到https://fjoppe.github.io/Legivel/个支持df.groupby('name').tail(1) Out[25]: Temp Timestamp name value 1 1 2011-01-01 00:00:15 A 2 5 5 2011-01-01 00:01:15 B 1 8 8 2011-01-01 00:02:00 C 3 的文件
教程https://fjoppe.github.io/Legivel/tutorial.html
作者https://github.com/fjoppe/Legivel

  

使用F#的Yaml到本机处理器,产生F#类型。

     

此示例演示了如何使用此库将Yaml转换为Native:

     
df.drop_duplicates('name',keep='last')
Out[26]: 
   Temp           Timestamp name  value
1     1 2011-01-01 00:00:15    A      2
5     5 2011-01-01 00:01:15    B      1
8     8 2011-01-01 00:02:00    C      3
     

然后

     
.NET Standard 2.0
     

这将导致:

     
#r "Legivel.Parser.dll"
#r "Legivel.Mapper.dll"
open Legivel.Serialization

type PlayEvent = {
  time   : string
  player : string
  action : string
}

//  example : http://www.yaml.org/spec/1.2/spec.html#id2760519
let yaml = "
---
time: 20:03:20
player: Sammy Sosa
action: strike (miss)
...
---
time: 20:03:47
player: Sammy Sosa
action: grand slam
..." 
相关问题