我有一些JSON数据,我试图反序列化,看起来像这样:
{[{
"node":"xyz",
"type":"string"
},
{
"node":{ "moredata":"values", "otherdata":"values2" },
"type":"node"
}]}
我希望能够将其序列化为f#类,但它目前失败,因为节点字段可以更改类型。有人知道处理这个的好方法吗?假设节点字段是JsonNode类型?我目前正在使用Newtonsoft,但如果其中一个有更好的解决方案,可以使用其他库。
编辑:不幸的是,我不能使用类型提供程序,因为我希望这在c#中可用。
您可以使用JSON类型提供程序。它理解异构节点类型。下面是来自OP的JSON(已更正,因此它是有效的JSON),用作定义类型的示例:
open FSharp.Data
type MyJson = JsonProvider<"""{
"arr": [
{
"node": "xyz",
"type": "string"
},
{
"node": {
"moredata": "values",
"otherdata": "values2"
},
"type": "node"
}
]
}""">
您现在可以通过调用MyJson.Parse
来创建该类型的值,但是如果您只想查看作为推断类型的示例使用的JSON,您也可以使用GetSample
:
let json = MyJson.GetSample()
现在您可以开始探索数据了。这是一个FSI会话:
> json.Arr;;
val it : JsonProvider<...>.Arr [] =
[|{
"node": "xyz",
"type": "string"
};
{
"node": {
"moredata": "values",
"otherdata": "values2"
},
"type": "node"
}|]
> json.Arr |> Array.map (fun x -> x.Node);;
val it : JsonProvider<...>.StringOrNode [] =
[|"xyz"; {
"moredata": "values",
"otherdata": "values2"
}|]
可以看到,每个Node
都是一个StringOrNode
值,可以这样访问:
> json.Arr |> Array.map (fun x -> x.Node.Record) |> Array.choose id;;
val it : JsonProvider<...>.Node [] =
[|{
"moredata": "values",
"otherdata": "values2"
}|]
由于x.Node.Record
是option
,所以只能选择Some
和Array.choose
。
你可以用同样的方式获得字符串:
> json.Arr |> Array.map (fun x -> x.Node.String) |> Array.choose id;;
val it : string [] = [|"xyz"|]
你看过FSharp了吗?数据JSON解析器(不是类型提供程序,而是解析器)?如果您自己定义类,则可以使用解析器遍历属性并填充类。
http://fsharp.github.io/FSharp.Data/library/JsonValue.html下面是一个工作示例。
open System
open FSharp.Data
open FSharp.Data.JsonExtensions
let json = """{
"arr": [
{
"node": "xyz",
"type": "string"
},
{
"node": {
"moredata": "values",
"otherdata": "values2"
},
"type": "node"
}
]
}"""
type Node =
| String of string
| Complex of string*string
with
static member Parse (json:JsonValue) =
match json with
| JsonValue.String (s) -> String(s)
| JsonValue.Record (r) ->
r
|> Map.ofArray // Map<string,JsonValue>
|> fun m -> Complex(m.["moredata"].AsString(),m.["otherdata"].AsString())
| _ -> raise (new Exception("Can't parse"))
type Simple =
{ Node:Node; Type:string}
with
static member Parse (json:JsonValue) =
// ideally we'd use json?node and json?type to access the properties, but since type is a reserved word.
{Node=Node.Parse(json?node); Type=json.GetProperty("type").AsString()}
[<EntryPoint>]
let main argv =
let s=
json
|> JsonValue.Parse
|> fun j ->
seq { for v in j?arr do yield Simple.Parse v}
|> Array.ofSeq
printfn "%A" s
0
输出:[|{Node = String "xyz";
Type = "string";}; {Node = Complex ("values","values2");
Type = "node";}|]
也许你可以这样写:
open Newtonsoft.Json
open Newtonsoft.Json.Linq
open System.Linq
let json = """{
"arr": [
{
"node": "xyz",
"type": "string"
},
{
"node": {
"moredata": "values",
"otherdata": "values2"
},
"type": "node"
}
]
}"""
type Simple = {
Node : string
Type : string
}
JObject.Parse(json).["arr"]
.Children()
.Select(fun (x:JToken) ->
{Node = string (x.ElementAt 0) ; Type = string (x.ElementAt 1)})
.ToList()
.ForEach(fun x -> printfn "Type: %snNode:n%s" x.Type x.Node)
打印:
Type: "type": "string"
Node:
"node": "xyz"
Type: "type": "node"
Node:
"node": {
"moredata": "values",
"otherdata": "values2"
}
链接:https://dotnetfiddle.net/3G1hXl
但是它是c#的风格。不适合f#
这是我目前使用的解决方案:
type Object = {
node : Newtonsoft.Json.Linq.JToken
``type`` : string
}
let data = JsonConvert.DeserializeObject<Object[]>(jsonStr)
这至少允许我加载对象并在不丢失任何数据的情况下具有一定的类型安全性。
但我的理想解决方案将允许我有类似node: ICommonInterface的东西然后底层类型将是每个数据类型的类型