首先,获取模式并解析:
myuser@my-laptop:~/Documents/Projects/frontend$ npm list --depth 0
frontend@0.0.1 /home/myuser/Documents/Projects/frontend
├── async@0.9.2
├── bower@1.6.3
├── clean-css@2.2.23
├── del@1.2.1
├── gulp@3.9.0
├── gulp-angular-templatecache@1.8.0
├── gulp-concat@2.6.0
├── gulp-cordova-bump@1.3.0
├── gulp-minify-css@0.3.13
├── gulp-ng-annotate@1.1.0
├── gulp-rename@1.2.2
├── gulp-replace-task@0.1.0
├── gulp-sass@1.3.3
├── gulp-useref@1.3.0
├── gulp-util@2.2.20
├── jshint@2.8.0
├── mv@2.1.1
├── ng-annotate@0.15.4
├── q@1.4.1
├── shelljs@0.3.0
├── uglify-js@2.4.14
└── yargs@1.3.3
现在我们可以访问type desc = JsonProvider< """[{"name": "", "age": 1}]""", InferTypesFromValues=true >
let json = """[{"name": "Kitten", "age": 322}]"""
let typedJson = desc.Parse(json)
.Age和.Name属性,但是,我希望在编译时对它们进行模式匹配,以便在架构发生更改时出错。
由于这些属性被删除,我们无法在运行时获取它们:
typedJson.[0]
...我使用活动模式制作了运行时检查版本:
let ``returns false``() =
typedJson.[0].GetType()
.FindMembers(MemberTypes.All, BindingFlags.Public ||| BindingFlags.Instance,
MemberFilter(fun _ _ -> true), null)
|> Array.exists (fun m -> m.ToString().Contains("Age"))
理论上,如果let (|Name|Age|) k =
let toID = NameUtils.uniqueGenerator NameUtils.nicePascalName
let idk = toID k
match idk with
| _ when idk.Equals("Age") -> Age
| _ when idk.Equals("Name") -> Name
| ex_val -> failwith (sprintf "\"%s\" shouldn't even compile!" ex_val)
typedJson.[0].JsonValue.Properties()
|> Array.map (fun (k, v) ->
match k with
| Age -> v.AsInteger().ToString() // ...
| Name -> v.AsString()) // ...
|> Array.iter (printfn "%A")
不是OS,我将无法实施FSharp.Data
。一般来说,整个方法似乎是错误的,并重做了工作。
我知道使用类型提供程序无法生成受歧视的联合,但是在编译时可能有更好的方法来完成所有这些检查吗?
答案 0 :(得分:3)
据我所知,无法确定Json架构是否已经改变&#34;在编译时使用给定的TP。
这就是原因:
JsonProvider<sample>
正是在编译时启动的,它提供了一种在运行时操作Json内容的类型。这个提供的擦除类型有几个适用于任何sample
和类型Root
的运行时静态方法
使用少量实例属性扩展IJsonDocument
,包括基于编译时提供的样本(在您的情况下,属性Name
和Age
)。只有一个非常宽松的隐式
Json&#34;架构&#34;在JsonProvider
之后 - 提供类型,没有其他这样的实体可以与编译时的更改进行比较;
desc
和带有对应实例方法的Root
类型
为您操作任意Json内容提供服务。所有这些爵士乐都非常不可知
&#34; Json schema&#34;,在你给定的情况下,只要运行时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}]]""")
两者都将在运行时被愉快地解析为&#34;键入Json&#34;符合&#34;架构&#34;由给定sample
的TP派生而来,虽然显然Name
和Age
缺失,但如果访问则会引发异常。
在这种情况下改变了 如果提供的代码中使用的访问器与更改不兼容,则引用的模式可能会破坏编译。 伴随运行时Json有效载荷验证器或验证解析器的这种安排可以提供可靠的企业质量 Json架构变更管理。
来自JsonProvider
的 Fsharp.Data
TP缺少此类Json架构处理功能,因此有效负载验证仅在运行时完成。
答案 1 :(得分:2)
如果你使用InferTypesFromValues=false
,你会得到一个强大的类型:
type desc = JsonProvider< """[{"name": "", "age": 1}]""", InferTypesFromValues=false >
您可以使用desc
类型在您关注的属性上定义活动模式:
let (|Name|_|) target (candidate : desc.Root) =
if candidate.Name = target then Some target else None
let (|Age|_|) target (candidate : desc.Root) =
if candidate.Age = target then Some target else None
这些活动模式可以这样使用:
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”。
答案 2 :(得分:2)
引用您的评论,以更好地解释您想要实现的目标:
谢谢!但我想要实现的是获得编译器错误 如果我添加一个新字段,例如为json架构着色然后忽略它 而后来处理。如果是工会,它将是即时FS0025。
和:
是的,我必须处理所有字段,所以我不能依赖_。我想要它 当架构发生变化时,我的F#程序无需添加即可编译 必要的处理功能(而不仅仅是忽略新的领域或 在运行时崩溃。)
最简单的解决方案是建立一个&#34;测试&#34;对象
提供的类型带有两个构造函数:一个使用JSonValue并对其进行解析 - 实际上与JsonValue.Parse
相同 - 而另一个需要每个字段都要填充。
那是我们感兴趣的人。
我们还将使用命名参数调用它,这样我们不仅可以安全地添加或删除字段,而且还可以重命名或更改字段
type desc = JsonProvider< """[{"name": "SomeName", "age": 1}]""", InferTypesFromValues=true >
let TestObjectPleaseIgnore = new desc.Root (name = "Kitten", age = 322)
// compiles
(请注意,我将示例中的name
值更改为"SomeName"
,因为""
被推断为通用JsonValue
。)
现在,如果类型提供程序使用的示例中突然出现更多字段,则构造函数将变得不完整,无法编译。
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.
显然,错误是指1参数构造函数,因为它是它试图适合的构造函数,但是你会看到现在提供的类型有一个3参数构造函数替换2参数之一。
答案 3 :(得分:2)
<tldr>
Build some parser to handle your issues. http://www.quanttec.com/fparsec/
</tldr>
因此...
你想要能够阅读某些内容并使用它做某事的东西。不知道apriori这些东西是什么。
祝你好运。
您不希望类型提供商为您执行此操作。类型提供程序的完全目的是“在编译时这是我所看到的,这就是我将使用的”。
随着说:
您需要一些其他类型的解析器,您可以在其中检查“架构”(您知道的内容或您上次看到的内容与实际内容的定义)。实际上,一些动态解析器将数据转换为动态类型的动态结构。
请注意,动态不是静态的。 F#有静态类型,很多都基于此。并提供类型提供商。战斗会让你的头疼。这当然是可能的,甚至有可能通过打击类型提供者来实际使用这种方法,但它再次不是真正的类型提供者,也不是它的目的。