Как вы десериализуете JSON в карту / словарь записей в F #? - PullRequest
1 голос
/ 18 апреля 2019

Вот форма JSON:

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

Я бы хотел десериализовать его в нечто вроде типа Map<String, Person>, где Person и Address - это записи.Имя человека должно быть ключом в Map.

, который я пробовал FSharp.Data JsonProvider, но он дает каждому человеку свой собственный тип, например:

enter image description here

Есть ли библиотека, которая лучше справляется с этой ситуацией?

Ответы [ 3 ]

3 голосов
/ 18 апреля 2019

Если вы не хотите (или не можете) использовать FSharp.Data по какой-либо причине, вы также можете попробовать Thoth.Json , который доступен как для Fable, так и для обычного .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 голос
/ 19 апреля 2019

Как указано в комментариях, проблема в том, что ваша структура JSON использует запись для представления списка людей (с именами в качестве ключа).Если вы можете изменить его, как предложено в комментарии, то это, вероятно, лучший подход.

В качестве альтернативы, вы все равно можете прочитать это с помощью F # Data, используя провайдер типов для определения типа для человека:

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

Теперь, предполагая, что input является вашей входной строкой с несколькими людьми, вы можете прочитать ее с помощью парсера JsonValue, выполнить итерации по всем полям записей верхнего уровня вручную, но затем проанализировать отдельных людей в вашем JSONиспользуя ваш новый Person тип:

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

Это дает вам список кортежей, содержащих имя и типизированный объект для каждого человека (и вы получаете обычные преимущества поставщиков типов, то есть вы можете получить доступ к адресуи возраст, используя .)

1 голос
/ 18 апреля 2019

Вы по-прежнему можете использовать FSharp.Data's 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"
  }
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...