2016-05-16 2 views
3

J'essaye d'analyser JSON pour produire un type avec plusieurs constructeurs. Le défi est que le type est codé dans le nom d'une clé qui contient les données requises. En théorie, je pourrais utiliser un tas d'appels .:? et ensuite vérifier si la clé donnée renvoie Just mais je pense qu'il doit y avoir une meilleure façon. J'ai regardé asum mais cela ne m'a pas aidé beaucoup (probablement à cause de mon manque de familiarité avec cela).Aeson analyse dans plusieurs constructeurs

import Data.Aeson 
import Data.Time.Clock 

data Request = Req1 { id :: String, properties :: Value } 
      | Req2 { id :: String, properties :: Value } 
      | Req3 { id :: String, time :: UTCTime } 

instance FromJSON Request where 
    parseJSON = withObject "message" $ \o -> 
    -- ??? 

demandes Exemple:

{"req1": {"id": "345", "p1": "v1", "p2": "v2"}} 

{"req2": {"id": "654", "p3", "v3"}} 

{"req3": {"id": "876", "time": 1234567890}} 
+1

Ne pouvez-vous pas utiliser un type différent pour chaque objet requête distinct? – jkeuhlen

+0

Voir aussi http://stackoverflow.com/questions/32421836/aeson-parsing-dynamic-keys-as-type-field?rq=1 –

Répondre

2

Voici comment inspecter manuellement un objet:

{-# LANGUAGE OverloadedStrings #-} 

import Data.Aeson 
import Data.Time.Clock 
import qualified Data.HashMap.Strict as H 
import Control.Monad 

type Val = Int 

data Request = Req1 { id :: String, properties :: Val } 
      | Req2 { id :: String, properties :: Val } 
      | Req3 { id :: String, time :: UTCTime } 

instance FromJSON Request where 
    parseJSON (Object v) = 
    case H.lookup "req1" v of 
     Just (Object h) -> Req1 <$> h .: "id" <*> h .: "properties" 
     Nothing -> 
     case H.lookup "req2" v of 
      Just (Object h) -> Req2 <$> h .: "id" <*> h .: "properies" 
      Nothing -> 
      case H.lookup "req3" v of 
       Just (Object h) -> Req3 <$> h .: "id" <*> h .: "time" 
       Nothing -> mzero 

Si la clé existe req1 il suppose qu'il est une valeur Req1; sinon, si la clé req2 existe, elle essayera de l'analyser comme une valeur Req2; etc. pour req3. Si aucune de ces clés n'existe, cela échouera. Vous pouvez également utiliser fail "..." pour afficher un message d'erreur personnalisé.