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