在处理这类问题时,我发现尽可能长时间地呆在强类型的世界中更容易。如果我们使用
JsonValue.Properties
从一开始,类型提供程序就没有多少价值。如果数据过于动态,我宁愿使用另一个JSON库,例如。
Newtonsoft.Json文件
.
首先,让我们定义一些常量:
open FSharp.Data
let [<Literal>] Object = """{
"dataType": "int",
"constraints": {
"min": 0,
"max": 650,
"scaling": -10,
"steps": 1
},
"defaultValue": "string"
}"""
let [<Literal>] MultiDimArray = """{
"dataType": "enum",
"constraints": {
"names": [
["n.a.", 1],
["OK", 4],
["High Warn", 6],
["Too Low", 7],
["Too High", 8],
["Low Warn", 9]]
},
"defaultValue": "4"
}"""
let [<Literal>] Sample = "["+Object+","+MultiDimArray+"]"
type RawDescription = JsonProvider<Sample, SampleIsList = true>
type Description(description) =
let description = RawDescription.Parse(description)
let objectConstraints =
let c = description.Constraints
[
"Min", c.Min
"Max", c.Max
"Scaling", c.Scaling
"Steps", c.Steps
]
// Convert (name, value option) -> (name, value) option
// and filter for `Some`s (i.e. pairs having a value)
|> Seq.choose (fun (n, v) -> v |> Option.map (fun v -> n, v))
let namedConstraints =
description.Constraints.Names
|> Seq.map (fun arr ->
match arr.JsonValue.AsArray() with
| [| n; v |] -> n.AsString(), v.AsInteger()
| _ -> failwith "Unexpected `names` structure")
member __.DataType = description.DataType
member __.DefaultValue =
// instead of this match we could also instruct the type provider to
// not infer types from values: `InferTypesFromValues = false`
// which would turn `DefaultValue` into a `string` and all numbers into `decimal`s
match description.DefaultValue.Number, description.DefaultValue.String with
| Some n, _ -> n.ToString()
| _, Some s -> s
| _ -> failwith "Missing `defaultValue`"
// Map<string,int>
member __.Constraints =
objectConstraints |> Seq.append namedConstraints
|> Map
然后,用法如下所示:
// map [("Max", 650); ("Min", 0); ("Scaling", -10); ("Steps", 1)]
Description(Object).Constraints
// map [("High Warn", 6); ("Low Warn", 9); ("OK", 4); ("Too High", 8); ("Too Low", 7); ("n.a.", 1)
Description(MultiDimArray).Constraints