我目前正在尝试使用F#JsonProvider对从REST API收到的一组Json对象进行反序列化。
这已经适用于大部分零件,但是对象包含可具有不同内容的嵌套项目。
一旦它可以是普通的JsonObject,如
{
"dataType": "int",
"constraints": {
"min": 0,
"max": 650,
"scaling": -10,
"steps": 1
},
"defaultValue": "string",
}
但它也可以是一个多维数组,如
{
"dataType": "enum",
"constraints": {
"names": [["n.a.", 1],
["OK", 4],
["High Warn", 6],
["Too Low", 7],
["Too High", 8],
["Low Warn", 9]]
},
"defaultValue": "4",
}
在我提供的类型中,我想公开这样的约束
type Description (descriptionJsonIn: string) =
let parsedInfo = DescriptionProvider.Parse(descriptionJsonIn)
let parsedConstraints =
match parsedInfo.Constraints.JsonValue.Properties().[0].ToString() with
//| "names" ->
//parsedInfo.Constraints.JsonValue.Properties
//|> Array.map (fun x -> x.ToValueTuple)
//|> dict<string,string>
| "min" ->
parsedInfo.Constraints.JsonValue.Properties()
|> Seq.map (fun (k,v) -> k,v.AsString())
|> dict
| "maxLength" ->
parsedInfo.Constraints.JsonValue.Properties()
|> Seq.map (fun (k,v) -> k,v.AsString())
|> dict
| _ -> dict["",""]
member __.DataType = parsedInfo.DataType
member __.DefaultValue = parsedInfo.DefaultValue
member __.Constraints = parsedConstraints
我觉得解决方案应该类似于(无效)
| "names" ->
parsedInfo.Constraints.JsonValue.Properties()
|> Seq.map (fun (x) -> fst(x.ToValueTuple()).ToString(), snd(x.ToValueTuple()).ToString() )
|> dict<string,string>
但是我不知道足够的F#语法来继续搜索。我一直得到相同的结果:(
现在的问题:如何从parsedInfo.Constraints.JsonValue.Properties()
转到我想返回的字典?
[接受答案后更新]
接受的答案是并且是正确的。 但是,由于需求的变化,我不得不进行一些调整,因为存在多个由多个属性表示的约束类型。
我最终以
let objectConstraints =
let c = parsedVariableDescription.Constraints
if c.ToString().Contains("\"min\":")
then
[
"Min", c.Min
"Max", c.Max
"Scaling", c.Scaling
"Steps", c.Steps
]
|> Seq.choose (fun (n, v) -> v |> Option.map (fun v -> n, v.ToString()))
else if c.ToString().Contains("\"maxLen\":")
then
[
"RegExpr", c.RegExpr
"MaxLen", c.MaxLen
]
|> Seq.choose (fun (n, v) -> v |> Option.map (fun v -> n, v.ToString()))
else
Seq.empty
let namedConstraints =
parsedVariableDescription.Constraints.Names
|> Seq.map (fun arr ->
match arr.JsonValue.AsArray() with
| [| n; v |] -> n.AsString(), v.AsString()
| _ -> failwith "Unexpected `names` structure")
我现在可以将所有内容作为字符串返回,因为使用结果的那部分必须处理转换数据。
答案 0 :(得分:2)
在解决这类问题时,我发现更容易留在强类型的世界中,尽可能长的时间。如果我们从一开始就使用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