json - 如何将嵌套的 Json 数组反序列化为字典?
问题描述
我目前正在尝试使用 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")
我现在可以将所有内容都作为字符串返回,因为将使用结果的部分无论如何都必须处理转换数据。
解决方案
在处理这类问题时,我发现尽可能长时间地留在强类型世界中更容易。如果我们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
推荐阅读
- binary-search-tree - 与排序的键值对数组相比,平衡搜索树有什么好处?
- javascript - 使用管理员、供应商角色反应 javascript 构建代码的特定部分
- maven - @RunWith(CucumberWithSerenity.class) 的类型不匹配错误
- python - 运行烧瓶应用程序时不显示 HTML 表格边框
- java - 为什么我不能在没有 \n 的情况下运行这个 tcp 客户端套接字代码?
- python-3.x - 在 Google Cloud Shell 上更新 Python 版本
- go - 进入通道无限循环不阻塞
- javascript - Javascript ...使用新数组值的休息运算符行为?
- r - R中负二项式回归中的警告消息(向前逐步和向后逐步)
- javascript - 有没有办法让 wordpress 帖子类别具有功能但不可见?