F# 提供的类型上的模式匹配

F# 提供的类型上的模式匹配,f#,type-providers,f#-data,F#,Type Providers,F# Data,首先,获取一个模式并解析: type desc=JsonProvider 让json=“”[{“name”:“Kitten”,“age”:322}]“” 让typedJson=desc.Parse(json) 现在我们可以访问typedJson。[0].Age和.Name属性,但是,我希望在编译时对它们进行模式匹配,以便在模式更改时获得错误 由于这些属性已被擦除,我们无法在运行时获取它们: let``返回false```()= typedJson[0]。GetType() .FindMembe

首先,获取一个模式并解析:

type desc=JsonProvider<“”“[{”name:““age”:1}]”,InferTypesFromValues=true>
让json=“”[{“name”:“Kitten”,“age”:322}]“”
让typedJson=desc.Parse(json)
现在我们可以访问
typedJson。[0]
.Age和.Name属性,但是,我希望在编译时对它们进行模式匹配,以便在模式更改时获得错误

由于这些属性已被擦除,我们无法在运行时获取它们:

let``返回false```()=
typedJson[0]。GetType()
.FindMembers(MemberTypes.All、BindingFlags.Public | | | BindingFlags.Instance、,
MemberFilter(趣味->真),空)
|>Array.exists(fun m->m.ToString().Contains(“Age”))
…我已使用活动模式创建了运行时检查版本:

let(| Name | Age |)k=
设toID=NameUtils.uniqueGenerator NameUtils.nicePascalName
设idk=toidk
将idk与
|_uk.等于(“年龄”)时->年龄
|_uk.等于时(“名称”)->Name
|ex\u val->failwith(sprintf“\%s\”甚至不应该编译!(ex\u val)
typedJson[0].JsonValue.Properties()
|>Array.map(乐趣(k,v)->
匹配
|年龄->v.AsInteger().ToString()/。。。
|名称->v.AsString())/。。。
|>Array.iter(printfn“%A”)
理论上,如果
FSharp.Data
不是操作系统,我就无法实现
toID
。总的来说,整个方法似乎是错误的,需要重做工作


我知道不能使用类型提供程序生成有区别的联合,但也许有更好的方法在编译时执行所有这些检查?

如果使用
InferTypesFromValues=false
,您将返回一个强类型:

type desc = JsonProvider< """[{"name": "", "age": 1}]""", InferTypesFromValues=false >
这些活动模式可以这样使用:

let json = """[{"name": "Kitten", "age": 322}]"""
let typedJson = desc.Parse(json)

match typedJson.[0] with
| Name "Kitten" n -> printfn "Name is %s" n
| Age 322m a -> printfn "Age is %M" a
| _ -> printfn "Nothing matched"

给定这里的
typedJson
值,匹配项将打印出“Name is Kitten”。

据我所知,使用给定的TP无法在编译时找出“Json模式是否已更改”

这就是为什么:

  • JsonProvider
    正是在编译时启动的,它提供了一种在运行时操作Json内容的类型。提供的已擦除类型具有两种运行时静态方法,这两种方法对于任何
    sample
    和type
    Root
    使用少量实例属性扩展
    IJsonDocument
    ,包括基于编译时提供的示例的属性(在您的示例中为-properties
    Name
    Age
    JsonProvider
    后面的Json“schema”——提供的类型,没有其他这样的实体可以在编译时进行比较以进行更改;
  • 在运行时仅提供type
    desc
    及其静态方法,以及其
    Root
    类型及其相应的实例方法 您可以随时处理任意Json内容。所有这些爵士乐在音乐方面几乎都是不可知论的 “Json模式”,在您给定的情况下,只要运行时Json内容代表一个数组,它的元素可能几乎是任意的。 比如说,

    type desc = JsonProvider<"""[{"name": "", "age": 1}]"""> // @ compile-time
    
    let ``kinda "typed" json`` = desc.Parse("""[]""") // @ run-time
    let ``another kinda "typed" json`` =
        desc.Parse("""[{"contents":"whatever", "text":"blah-blah-blah"},[{"extra":42}]]""")
    
    type desc=JsonProvider/@编译时
    让“`kinda”键入“json`=desc.Parse(“[]”)/@run-time
    让“另一种”类型的“json”=
    desc.Parse(“[{”contents:“whatever”,“text:“blah blah blah”},[{“extra”:42}]]”)
    
    两者在运行时都会被愉快地解析为“类型化Json”,符合TP从给定的
    示例
    派生的“模式”,尽管显然
    名称
    年龄
    缺失,如果被访问,将引发异常。
  • 构建另一个依赖于正式的Json TP是可行的。 在创建类型时,它可能使用模式存储库中对给定模式的引用,并允许操作元素 仅通过编译时从模式派生的提供的访问器访问解析的Json负载

    在这种情况下,更改 如果代码中使用的访问器与更改不兼容,则引用的架构可能会中断编译。 这种安排伴随着运行时Json有效负载验证器或验证解析器,可以提供可靠的企业质量 Json模式更改管理


    JsonProvider
    TP from
    Fsharp.Data
    缺少这样的Json模式处理能力,因此负载验证只能在运行时完成。

    引用您的评论,其中更好地解释了您试图实现的目标:


    谢谢大家!!但我要做的是得到一个编译器错误 如果我在json模式中添加一个新字段,例如颜色,然后忽略它 而后期处理。如果是工会,则为即时FS0025

    以及:


    是的,我必须处理所有字段,所以我不能依赖于。我希望如此 当模式更改时,如果不添加 必要的处理功能(而不仅仅是忽略新字段或 运行时崩溃)

    最简单的解决方案是构造一个“测试”对象

    提供的类型带有两个构造函数:一个接受一个JSonValue并对其进行解析-实际上与
    JSonValue.Parse
    -相同,而另一个需要填写每个字段

    这才是我们感兴趣的

    我们还将使用命名参数调用它,这样我们不仅在添加或删除字段时安全,而且在重命名或更改字段时也安全

    type desc = JsonProvider< """[{"name": "SomeName", "age": 1}]""", InferTypesFromValues=true >
    
    let TestObjectPleaseIgnore = new desc.Root (name = "Kitten", age = 322)
    // compiles
    
    显然,该错误引用的是1参数构造函数,因为它试图拟合该构造函数,但您会看到,现在提供的类型有一个3参数构造函数取代了2参数构造函数。

    
    
    <tldr>
    Build some parser to handle your issues. http://www.quanttec.com/fparsec/
    </tldr>
    
    构建一些解析器来处理您的问题。http://www.quanttec.com/fparsec/
    所以

    你想要的东西可以读一些东西并用它做一些事情。不知道这些东西是什么

    祝你好运

    您不希望类型提供程序为您执行此操作。类型提供程序的全部目的是“在公司”
    type desc = JsonProvider< """[{"name": "SomeName", "age": 1, "color" : "Red"}]""", InferTypesFromValues=true >
    
    let TestObjectPleaseIgnore = new desc.Root (name = "Kitten", age = 322)
    // compilation error: The member or object constructor 'Root' taking 1 arguments are not accessible from this code location. All accessible versions of method 'Root' take 1 arguments.
    
    <tldr>
    Build some parser to handle your issues. http://www.quanttec.com/fparsec/
    </tldr>