2016-05-16 4 views
3

Я пытаюсь разобрать JSON для создания типа с несколькими конструкторами. Проблема состоит в том, что тип кодируется в имени ключа, который содержит требуемые данные. В теории я мог бы использовать кучу .:? звонков, а затем проверить, возвращает ли данный ключ Just, но я думаю, что должен быть лучший способ. Я посмотрел на asum, но это не очень помогло мне (возможно, из-за моей незнакомости с ним).Анализ Aeson на несколько конструкторов

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 -> 
    -- ??? 

Пример запросов:

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

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

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

Можете ли вы не использовать другой тип для каждого отдельного объекта запроса? – jkeuhlen

+0

Также см. Http://stackoverflow.com/questions/32421836/aeson-parsing-dynamic-keys-as-type-field?rq=1 –

ответ

2

Вот как вручную проверять объекта:

{-# 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 

Если ключ req1 существует, то он будет считать, что это значение Req1; else, если существует ключ req2, он попытается проанализировать его как значение Req2; и т. д. для req3. Если ни один из этих ключей не существует, он потерпит неудачу.

Вместо mzero вы также можете использовать fail "..." для отображения пользовательского сообщения об ошибке.

Смежные вопросы