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

Haskell 如何为具有多个无参数构造函数的代数数据类型创建ToJSON/FromJSON条目?,haskell,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

鉴于这些数据:

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"]’
toJSON和parseJSON函数各有一个参数,toJSON最简单的方法可能是:

instance ToJSON A1 where
    toJSON A1 = object ["tag" .= "A1"]
    toJSON A2 = object ["tag" .= "A2"]
    toJSON A3 = object ["tag" .= "A3"]
然后您只需在parseJSON中执行相反的操作:

这只是提取key标记处的值,然后对返回的文本执行模式匹配,以确定使用哪个构造函数。

toJSON和parseJSON函数各有一个参数,toJSON最简单的方法可能是:

instance ToJSON A1 where
    toJSON A1 = object ["tag" .= "A1"]
    toJSON A2 = object ["tag" .= "A2"]
    toJSON A3 = object ["tag" .= "A3"]
然后您只需在parseJSON中执行相反的操作:


这只是提取键标记处的值,然后对返回的文本执行模式匹配,以确定使用哪个构造函数。

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

.=的右侧是字符串文字,因此可以是字符串、文本或其他-使用::Text对其进行注释,或者像我下面所做的那样使用Text.pack。显示 :的结果是多态的,并且大小写模式都是导致相同问题的字符串文本。在下面的示例中,我将::Text添加到标记中,从而消除了歧义。 完整代码:

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

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

.=的右侧是字符串文字,因此可以是字符串、文本或其他-使用::Text对其进行注释,或者像我下面所做的那样使用Text.pack。显示 :的结果是多态的,并且大小写模式都是导致相同问题的字符串文本。在下面的示例中,我将::Text添加到标记中,从而消除了歧义。 完整代码:

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

JSON的转换取决于您,通常我希望某种类型的标记指示构造函数。toJSON A1->{'tag':'A1'}等等。到JSON的转换取决于您,通常我希望某种类型的标记指示构造函数。toJSON A1->{'tag':'A1'}等等。在实例toJSON A1中,A1是类的名称,而接下来的3个是构造函数,对吗?也就是说,第二个A1是构造函数。@AlanCoromano第一个A1是数据类型的名称,类在Haskell中具有不同的特定含义。接下来的三个是构造函数。最好使用数据A=A1 | A2 | A2和实例A,其中。。。JSON A中的实例,…@AlanCoromano在你的问题中,你说数据A1=A1 | A2 | A3,所以术语A1既是一个类型又是一个构造函数。您的评论使用了术语class,它在Haskell中表示其他内容。@AlanCoromano您说过第一个A1是类的名称。Haskell中的类更像Java中的接口,而Java中的类更像Haskell中的数据类型,尽管您无法直接转换它们。我认为如果您的数据类型的名称是A,可能会更清楚,以避免与该类型的任何构造函数同名,尽管这是完全有效的Haskell,这样很容易在代码中看到哪个是哪个。@AlanCoromano关于类的名称是数据名称的评论,您应该在所使用的编程语言中使用专有名称。与大多数语言相比,数据A和类A在Haskell中具有非常非常不同的含义。数据A声明了一个新的具体数据类型,如Int,而类声明了一个可以由其他数据类型(如Monad、ToJSON或Num)满足的接口。它们是不可互换的。在实例ToJSON A1中,A1是类的名称,而接下来的3是构造函数,对吗?也就是说,第二个A1是构造函数。@AlanCoromano第一个A1是数据类型的名称,类在Haskell中具有不同的特定含义。接下来的三个是构造函数。最好使用数据A=A1 | A2 | A2和实例A,其中。。。JSON A中的实例,…@AlanCoromano在你的问题中,你说数据A1=A1 | A2 | A3,所以术语A1既是一个类型又是一个构造函数。您的评论使用了术语class,它在Haskell中表示其他内容。@AlanCoromano您说过第一个A1是类的名称。Haskell中的类更像Java中的接口,而Java中的类更像Haskell中的数据类型,尽管您无法直接转换它们。我认为,如果数据类型的名称是A,可能会更清楚,以避免与该类型的任何构造函数同名,尽管这是完全有效的Haskell,这样很容易在代码中看到哪个是哪个。@AlanCoromano关于名称的注释
如果类是数据的名称,那么在所使用的编程语言中应该使用专有名称。与大多数语言相比,数据A和类A在Haskell中具有非常非常不同的含义。data A声明了一个新的具体数据类型,如Int,而class声明了一个可由其他数据类型(如Monad、ToJSON或Num)满足的接口。它们是不可互换的。为什么在json中称之为tag?这是一个常用术语。您正在“标记”数据结构,以指示实际表达的是联合和类型的哪些可能值。我基本上是在你问题下的第一条评论中说的。你为什么用json称它为tag?这是一个常见的术语。您正在“标记”数据结构,以指示实际表达的是联合和类型的哪些可能值。我基本上是在我对你的问题的第一次评论中说的。