2
votes

Comment désérialiser JSON en une carte / dictionnaire d'enregistrements en F #?

Voici la forme du JSON:

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

Je voudrais le désérialiser en quelque chose comme le type Map Personne et Adresse sont des enregistrements. Le nom de la personne doit être la clé dans la Map .

J'ai essayé le JsonProvider code de FSharp.Data >, mais cela donne à chaque personne son propre type comme ceci:

entrez la description de l'image ici

Existe-t-il une bibliothèque qui gère mieux cette situation?


2 commentaires

Si vous avez le JSON sous votre contrôle, je le changerais en une liste d'objets personne. La version actuelle n'est pas très adaptée pour la mapper à ce que vous voulez. [{"name": "Alice", "Age": 30}, {"name": "Bob", "Age": 6}] serait facilement désérialisable par Fsharp.Data.


Malheureusement, je consomme une API externe, donc je ne peux pas contrôler sa structure.


3 Réponses :


2
votes

Vous pouvez toujours utiliser la JsonValue de FSharp.Data au lieu du fournisseur:

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

output:

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

p>


0 commentaires

3
votes

Si vous ne voulez pas (ou ne pouvez pas) utiliser FSharp.Data pour quelque raison que ce soit, vous pouvez également essayer Thoth.Json qui est disponible pour Fable ainsi que pour .NET normal. Dans Thoth, vous pouvez soit utiliser des Autoencoders / Autodecoder qui essaieront de gérer la conversion pour vous si vos types F # ressemblent étroitement au JSON utilisé, soit vous écrivez vos propres fonctions d'encodage / décodage.

Un exemple typé (comme une application console) qui devrait correspondre à votre cas d'utilisation (si je l'ai bien compris) pourrait ressembler à ceci.

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


0 commentaires

1
votes

Comme indiqué dans les commentaires, le problème est que votre structure JSON utilise un enregistrement pour représenter une liste de personnes (avec des noms comme clé). Si vous pouvez le modifier comme suggéré dans le commentaire, alors c'est probablement la meilleure approche.

Alternativement, vous pouvez toujours lire ceci avec F # Data en utilisant le fournisseur de type pour définir un type pour la personne:

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

Maintenant, en supposant que input est votre chaîne d'entrée avec plusieurs personnes, vous pouvez la lire en utilisant l'analyseur JsonValue , itérer sur tout le haut- les champs d'enregistrement de niveau manuellement, puis analyser les personnes individuelles dans votre JSON en utilisant votre nouveau type de Person :

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

Cela vous donne une liste de tuples contenant un nom et un objet typé pour chaque personne (et vous bénéficiez des avantages habituels des fournisseurs de types, c'est-à-dire que vous pouvez accéder à l'adresse et à l'âge en utilisant .)


1 commentaires

@DharmaTurtle si vous souhaitez continuer à utiliser le fournisseur de types pour l'exploration de données, cette réponse est la plus adaptée à votre cas d'utilisation. Merci Tomas d'avoir indiqué cette solution!