2017-07-03 4 views
0

je une structure JSON comme celui-ciAESON: Parse structure dynamique

{ 
    "tag1": 1, 
    "tag2": 7, 
    ... 
} 

Et j'avoir un type comme celui-ci

data TagResult { name :: String, numberOfDevicesTagged :: Int } deriving (Show, Eq) 
newtype TagResultList = TagResultList { tags :: [TagResult] } 

Les noms de balises sont bien sûr entièrement dynamique et je ne sais pas eux au moment de la compilation. Je voudrais créer une instance FromJSON pour analyser les données JSON mais je ne peux pas le faire compiler.

Comment puis-je définir parseJSON pour que cela se produise?

+0

Vous pouvez simplement utiliser les types 'FromJSON' et' ToJSON' existants pour 'Map'. Vos tags seraient alors les clés. –

+1

Copie possible de [FromJSON faire une liste à partir de plusieurs champs] (https://stackoverflow.com/questions/44514645/fromjson-make-a-list-from-multiple-fields) –

+0

C'est un cas différent de la marque [FromJSON une liste de plusieurs champs] (https://stackoverflow.com/questions/44514645/fromjson-make-a-list-from-multiple-fields). Le lien ci-dessus est pour un cas où la liste des étiquettes possibles est connue au moment de la compilation, dans ce cas elles sont inconnues. – Batou99

Répondre

1

Vous pouvez utiliser le fait que Object est un HasMap et extraire la clé au moment de l'exécution. Vous pouvez ensuite écrire l'instance FromJSON comme suit -

{-# LANGUAGE OverloadedStrings #-} 
module Main where 

import Data.Aeson 
import qualified Data.Text as T 
import qualified Data.HashMap.Lazy as HashMap 

data TagResult = TagResult { name :: String 
          , numberOfDevicesTagged :: Int 
          } deriving (Show, Eq) 


newtype TagResultList = TagResultList { tags :: [TagResult] } deriving Show 


instance ToJSON TagResult where 

    toJSON (TagResult tag ntag) = 
    object [ T.pack tag .= ntag ] 

instance ToJSON TagResultList where 

    toJSON (TagResultList tags) = 
    object [ "tagresults" .= toJSON tags ] 


instance FromJSON TagResult where 

    parseJSON (Object v) = 
    let (k, _) = head (HashMap.toList v) 
    in TagResult (T.unpack k) <$> v .: k 

    parseJSON _ = fail "Invalid JSON type" 

instance FromJSON TagResultList where 

    parseJSON (Object v) = 
    TagResultList <$> v .: "tagresults" 


main :: IO() 
main = do 

    let tag1 = TagResult "tag1" 1 
     tag2 = TagResult "tag2" 7 
     taglist = TagResultList [tag1, tag2] 

    let encoded = encode taglist 
     decoded = decode encoded :: Maybe TagResultList 

    print decoded 

Le programme ci-dessus doit imprimer la liste des résultats d'étiquette.

Just (TagResultList {tags = [TagResult {name = "tag1", numberOfDevicesTagged = 1},TagResult {name = "tag2", numberOfDevicesTagged = 7}]})