2016-05-16 26 views
3

我想解析JSON以產生具有多個構造函數的類型。挑戰在於該類型是以包含所需數據的密鑰的名稱進行編碼的。理論上,我可以使用一堆.:?調用,然後檢查給定的密鑰是否返回Just,但我認爲必須有更好的方法。我看着asum,但這對我沒有多大幫助(可能是因爲我不熟悉它)。艾森解析爲多個構造函數

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值;否則,如果鍵req2存在,它會嘗試將其解析爲Req2值;等等。req3。如果這些密鑰都不存在,它將會失敗。

而不是mzero您還可以使用fail "..."來顯示自定義的錯誤消息。