如何为具有多个不带参数的构造函数的代数数据类型创建ToJSON / FromJSON条目?

时间:2016-03-24 14:53:01

标签: haskell

鉴于此数据:

data A1 = A1 | A2 | A3

如何为它创建实例?

instance ToJSON A1 where
  toJSON = ???

instance FromJSON A1 where
  parseJSON = ???

如果它是一个构造函数,我能够做到这一点,但我无法弄清楚如何使用多个不接受任何参数的方法来做到这一点。

更新: 对于每个构造函数,我都有这些错误:

my-app/src/Lib.hs:54:32:
    No instance for (ToJSON a1) arising from a use of ‘.=’
    The type variable ‘a1’ is ambiguous
    Note: there are several potential instances:
      instance ToJSON UUID -- Defined in ‘Data.UUID.Aeson’
      instance ToJSON MyType -- Defined at src/Lib.hs:53:10
      instance ToJSON MyType2 -- Defined at src/Lib.hs:70:10
    In the expression: "tag" .= "A1"
    In the first argument of ‘object’, namely ‘["tag" .= "A1"]’
    In the expression: object ["tag" .= "A1"]

my-app/src/Lib.hs:54:35:
    No instance for (Data.String.IsString a1)
      arising from the literal ‘"A1"’
    The type variable ‘a1’ is ambiguous
    Note: there are several potential instances:
      instance Data.String.IsString Value
        -- Defined in ‘aeson-0.9.0.1:Data.Aeson.Types.Internal’
      instance (a
                ~ bytestring-0.10.6.0:Data.ByteString.Internal.ByteString) =>
               Data.String.IsString
                 (attoparsec-0.13.0.1:Data.Attoparsec.ByteString.Internal.Parser a)
        -- Defined in ‘attoparsec-0.13.0.1:Data.Attoparsec.ByteString.Char8’
      instance Data.String.IsString
                 bytestring-0.10.6.0:Data.ByteString.Builder.Internal.Builder
        -- Defined in ‘bytestring-0.10.6.0:Data.ByteString.Builder’
      ...plus 13 others
    In the second argument of ‘(.=)’, namely ‘"A1"’
    In the expression: "tag" .= "A1"
    In the first argument of ‘object’, namely ‘["tag" .= "A1"]’

2 个答案:

答案 0 :(得分:3)

toJSONparseJSON函数各自参数,toJSON最简单的可能是:

instance ToJSON A1 where
    toJSON A1 = object ["tag" .= "A1"]
    toJSON A2 = object ["tag" .= "A2"]
    toJSON A3 = object ["tag" .= "A3"]

然后你只需要在parseJSON中执行相反的操作:

instance FromJSON A1 where
    parseJSON (Object o) = do
        tag <- o .: "tag"
        case tag of
            "A1" -> return A1
            "A2" -> return A2
            "A3" -> return A3
            _    -> mzero

这只是提取键#34;标记&#34;的值。然后对返回的Text执行模式匹配,以确定要使用的构造函数。

答案 1 :(得分:2)

编辑中显示的问题都与类型歧义有关,可以通过使用函数应用程序使类型可推断或添加类型注释来解决。

  • .=的右侧是字符串文字,因此可能是StringText或其他人 - 使用::Text注释或者按照下面的操作进行注释并使用Text.pack . show
  • .:的结果是多态的,案例模式都是导致同样问题的字符串文字。在下面,我将:: Text添加到tag,从而消除了歧义。

完整的代码:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Data.Aeson.Types
import Control.Monad
import Data.Text as Text

data A1 = A1 | A2 | A3
  deriving (Eq, Ord, Show)


instance ToJSON A1 where
    toJSON a  = object ["tag" .= Text.pack (show a)]

instance FromJSON A1 where
    parseJSON (Object o) = do
        tag <- o .: "tag"
        case (tag :: Text) of
            "A1" -> return A1
            "A2" -> return A2
            "A3" -> return A3
            _    -> mzero
    parseJSON v = typeMismatch "A1" v