2015-03-26 1 views
0

J'ai des données qui sont une carte. Pour rendre la question plus concrète, supposons qu'elle soit représentée comme une assoc-list type D val = [(Key,val)] (ou comme type D val = Map Key val).Comment implémenter toJSON pour une assoc-list produisant un objet avec des paires clé-valeur de manière générique (en utilisant Aeson)?

  • Key est un type "enum" - une somme de constructeurs nulaires, .: par exemple

    Données clés = C1 | C2 | C3

  • Il doit y avoir instance ToJSON pour val.

Je voudrais mettre en œuvre

instance ToJSON val => ToJSON (D val) 

qui serait comme instance Map String val (la production d'un objet avec les paires clé-valeur correspondant) (an example, another example pour HashMap s), seulement plus générique.

Plus générique signifie que je ne suis pas limité à String ou Text comme clé (de telles instances pour Map se trouvent dans le paquet aeson).

Je veux apprendre à utiliser les mécanismes génériques dans aeson pour le faire de manière concise.

Aeson déjà peut convertir « énumérations » aux chaînes JSON, et aussi (C val1 val2 ...) à des objets avec une seule touche (le nom du constructeur, comme cela, je suppose: { "C":[val1,val2,...] }) avec genericToJSON defaultOptions{ sumEncoding = ObjectWithSingleField }. Et une autre conversion qui utilise les noms de constructeurs est avec les options par défaut pour les types de données avec des constructeurs non-nullary: alors le nom du constructeur devient la valeur d'une clé "tag". Je pourrais utiliser un code similaire.

Je pense que même une telle instance (pour les cartes dont les clés proviennent d'enums) serait utile à la bibliothèque. Mais AFAIU ni ni generic-aeson (qui a ajouté quelques options de conversion plus agréable) a une telle conversion prédéfinie.

+0

"Je pense que même une telle instance (pour les cartes dont les clés proviennent d'enums) serait utile à la bibliothèque" -> Je n'ai jamais eu besoin de cette instance, votre cas d'utilisation semble assez spécifique, je ne suis pas sûr il serait particulièrement utile d'avoir défini dans le paquet 'aeson'. Le vrai problème que je vois ici est la conversion de 'Map Key val' en' Map Text val', puis la sortie. Pourquoi ne pas simplement écrire une fonction qui convertit vos clés en un type qui utilise déjà 'ToJSON' au lieu de se soucier de la programmation générique? – bheklilr

+0

@bheklilr J'aime que les noms de constructeurs de Haskell soient réutilisés comme noms de clés. Je prévois de varier cette énumération lorsque je travaille sur mon projet, donc je ne veux pas maintenir les fonctions pour le texte <-> Conversions de clés (l'autre direction peut être nécessaire pour FromJSON, eh bien, pas nécessairement, car aeson peut vivre avec single ' Options pour les deux directions de conversion, ayant un seul 'fieldLabelModifier' pour les deux fronts, et je vois comment cela peut être fait pour une énumération.) –

+0

@bheklilr Je pensais que c'était en quelque sorte dans l'esprit d'Aeson de représenter beaucoup de données Haskell natives types en tant que JSON sans compter sur les conversions au texte fournies par l'utilisateur. Je trouve cela joyeux. –

Répondre

1

Un rapide et court, mais la solution est laide:

instance (ToJSON a) => ToJSON (D a) 
    where toJSON kvs = object [ t .= v | (k,v) <- kvs, let (String t) = toJSON k ] 

Avantages:

  • il réutilise les mécanismes génériques de Aeson pour obtenir la représentation clé (sous forme de texte);
  • il réutiliserait les mêmes règles de conversion pour les noms des champs ici comme défini dans le instance ToJSON Key, donc nous n'avons pas besoin de les écrire deux fois, ou de nous soucier de la cohérence.

Inconvénients:

  • conversion premier à Object juste pour extraire un String est tout simplement laid;
  • il introduit une conversion non totale dangereuse (qui peut même échouer si toJSON pour Key est mis en œuvre de manière non par défaut;.
  • il n'y a pas de contrôle de la compilation de temps qui Key est un ENUM fondé sur des types (En fait, , Je ne suis pas très désireux de comprendre les machines de type génériques, donc je vois que ces vérifications et choix peuvent être faits parce qu'eson est implémenté de cette façon, mais je ne serais pas capable de l'écrire moi-même.)