Aeson解析成多个构造函数

Aeson parsing into multiple constructors

我正在尝试解析 JSON 以生成具有多个构造函数的类型。挑战在于类型以包含所需数据的键的名称进行编码。理论上我可以使用一堆 .:? 调用然后检查是否给定密钥 returns 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}}

手动检查对象的方法如下:

{-# 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。如果 none 个键存在,它将失败。

除了 mzero,您还可以使用 fail "..." 来显示自定义错误消息。