我有一些JSON数据,我试图反序列化,看起来像这样:
{[{
"node":"xyz",
"type":"string"
},
{
"node":{ "moredata":"values", "otherdata":"values2" },
"type":"node"
}]}
我希望能够将其序列化为F#类,但它当前失败,因为节点字段可以更改类型。有谁知道处理这个的好方法?我假设我希望节点字段类似于JsonNode类型? 我目前正在使用Newtonsoft,但如果在其中一个中有更好的解决方案,则可以使用其他库。
编辑:不幸的是,我不能使用类型提供程序,因为我希望它可以在C#中使用。
答案 0 :(得分:5)
您可以使用JSON Type Provider。它了解异构节点类型。这是来自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"|]
答案 1 :(得分:2)
你看过FSharp.Data 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";}|]
答案 2 :(得分:1)
也许你可以适合这样的事情:
if (!EmailValidator.getInstance().isValid(email)) {
// ...
}
打印:
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: %s\nNode:\n%s" x.Type x.Node)
链接:https://dotnetfiddle.net/3G1hXl
但这是C#的风格。不适合F#
答案 3 :(得分:0)
以下是我目前使用的解决方案:
type Object = {
node : Newtonsoft.Json.Linq.JToken
``type`` : string
}
let data = JsonConvert.DeserializeObject<Object[]>(jsonStr)
这至少允许我加载对象并具有一些类型安全性,同时不会丢失任何数据。
但是我理想的解决方案可以让我拥有类似node:ICommonInterface的东西 然后底层类型将是每种数据类型的类型。