Как десериализовать JSON в карту / словарь записей в F #?

#json #parsing #f#

#json #синтаксический анализ #f#

Вопрос:

Вот форма 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 , но он дает каждому человеку свой собственный тип, например:

введите описание изображения здесь

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

Комментарии:

1. Если у вас есть JSON под вашим контролем, я бы изменил его на список объектов person. Текущая версия не очень подходит для сопоставления ее с тем, что вы хотите. [ {"name": "Alice", "Age": 30 }, {"name": "Bob", "Age": 6 }] было бы легко десериализовано с помощью Fsharp.Data.

2. К сожалению, я использую внешний API, поэтому я не могу контролировать его структуру.

Ответ №1:

Если вы не хотите (или не можете) использовать FSharp.Данные по любой причине вы также можете попробовать Thoth.Json, который доступен как для Fable, так и для normal .NET. В Thoth вы можете либо использовать автокодеры / автодекодеры, которые попытаются обработать преобразование для вас, если ваши типы F # очень похожи на используемые JSON, либо вы пишете свои собственные функции кодирования / декодирования.

Типизированный пример (как консольное приложение), который должен соответствовать вашему usecase (если я правильно понял), может выглядеть следующим образом.

 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
 

Ответ №2:

Вы все еще можете использовать FSharp.Данные 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"
  }
})
 

Ответ №3:

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

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

 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. @DharmaTurtle если вы хотите продолжать использовать поставщика типов для исследования данных, этот ответ наиболее подходит для вашего варианта использования. Спасибо, Томас, что указал на это решение!