您如何将JSON反序列化为F#中的记录的地图/字典?

时间:2019-04-18 12:28:57

标签: json parsing f#

这是JSON的形状:

{
  "Alice": {
    "Age": 30,
    "Address": {
      "City": "New York",
      "State": "NY"
    }
  },
  "Bob": {
    "Age": 6,
    "Address": {
      "City": "Chicago",
      "State": "IL"
    }
  }
}

我想将其反序列化为Map<String, Person>类型,其中PersonAddress是记录。人名应该是Map中的关键字。

我尝试过FSharp.Data的{​​{1}},但是它给每个人自己的类型,如下所示:

enter image description here

是否有一个可以更好地处理这种情况的库?

3 个答案:

答案 0 :(得分:3)

如果您出于任何原因不想(或不能)使用FSharp.Data,您还可以尝试Thoth.Json,它适用于寓言和普通的.NET。在Thoth中,您可以使用Autoencoders / Autodecoder,如果您的F#类型与使用的JSON非常相似,或者您编写了自己的编码/解码功能,则它将尝试为您处理转换。

一个适合您的用例(如果我正确理解的话)的键入示例(作为控制台应用程序)可能看起来像这样。

let testString = """
{
  "Alice": {
    "Age": 30,
    "Address": {
      "City": "New York",
      "State": "NY"
    }
  },
  "Bob": {
    "Age": 6,
    "Address": {
      "City": "Chicago",
      "State": "IL"
    }
  }
}
"""

open Thoth.Json.Net

type Address =
    { City : string
      State : string }

type Person =
    { Age : int
      Address: Address}

let decodeAddress : Decoder<Address> =
    Decode.object
        (fun get ->
            { City = get.Required.Field "City" Decode.string
              State = get.Required.Field "State" Decode.string })

let decodePerson : Decoder<Person> =
    Decode.object
        (fun get ->
            { Age = get.Required.Field "Age" Decode.int
              Address = get.Required.Field "Address" decodeAddress})

let decodeMap jsonString =
    Decode.fromString (Decode.keyValuePairs decodePerson) jsonString
    |> Result.map (fun namePersonList ->
        namePersonList
        |> Map.ofList)

[<EntryPoint>]
let main argv =
    match decodeMap testString with
    | Ok res -> printfn "%A" res
    | Error e -> printfn "%s" e
    0

答案 1 :(得分:1)

您仍然可以使用FSharp.Data的JsonValue代替提供程序:

JsonValue.Parse(json).Properties()
|> Seq.iter (printfn "%A")

输出:

("Alice",
 {
  "Age": 30,
  "Address": {
    "City": "New York",
    "State": "NY"
  }
})
("Bob",
 {
  "Age": 6,
  "Address": {
    "City": "Chicago",
    "State": "IL"
  }
})

答案 2 :(得分:1)

正如评论中指出的那样,问题在于您的JSON结构正在使用一条记录来表示人员列表(以名称为键)。如果您可以按照评论中的建议进行更改,那可能是最好的方法。

或者,您仍然可以通过使用类型提供程序为人员定义类型来使用F#Data进行读取:

type Person = JsonProvider<"""{
    "Age": 30,
    "Address": { "City": "New York", "State": "NY" }
  }""">

现在,假设input是您的多人输入字符串,则可以使用JsonValue解析器读取它,手动遍历所有顶级记录字段,然后解析您的个人使用新的Person类型的JSON:

[ for name, person in JsonValue.Parse(input).Properties() ->
    name, Person.Root(person) ]

这为您提供了一个元组列表,其中包含每个人的名称和类型化的对象(您将获得类型提供程序的通常好处,即,您可以使用.访问地址和年龄)