使用 Aeson 对 Json 中的基本求和类型进行序列化

Serialization of a basic sum type in Json with Aeson

type GoalDescription = Text

data GoalStatus = Created | Accomplished | InProgress | GivenUp deriving (Show , Eq , Generic )

data Goal = Goal {workspaceId ::WorkspaceId , goalId :: GoalId , description :: GoalDescription , status :: GoalStatus} deriving (Show , Eq , Generic )

instance ToJSON Goal where
  toJSON (Goal {workspaceId, goalId ,description,status } ) = object [
            "workspaceId" .= workspaceId,
            "goalId" .= goalId,
            "description" .= description,
            "status" .= status]

instance FromJSON Goal  where

    parseJSON (Object jsonObject) = Goal <$> jsonObject .: "workspaceId" <*>  jsonObject .: "goalId" <*>  jsonObject .: "description" <*>  jsonObject .: "status"
    parseJSON _ =  error $ "Json format not expected"

我想以这种方式实现 GoalStatus 的 FromJSON 和 ToJSON:Goal {.. status:"accomplished"}Goal {.. status:"inProgress"} 等等...不知何故我不知道如何实现这些类型 类 没有有一个键 -> 值结构... GoalStatus 应该只转换成 String Text 没有附加到值的键..

我有这个临时解决方案,我不得不添加一个名为 "value" 的不必要的密钥:

instance ToJSON GoalStatus where
    toJSON (Created) = object ["value" .= String "created"]
    toJSON (InProgress) = object ["value" .= String "inProgress"]
    toJSON (Accomplished) = object ["value" .= String "accomplished"]
    toJSON (GivenUp) = object ["value" .= String "GivenUp"]


instance FromJSON GoalStatus  where

  parseJSON (Object o) = do
     value <- o .: "value"
     case value of
          String status | (unpack status) == "created" -> return Created
          String status | (unpack status) == "inProgress" -> return InProgress
          String status | (unpack status) == "accomplished" -> return Accomplished
          String status | (unpack status) == "accomplished" -> return GivenUp
          _ -> error $ "Json format not expected"
  parseJSON _ =  error $ "Json format not expected"

String !TextValue 的构造函数,object 具有类型签名 [Pair] -> Value,其中 Pair(Text, Value)。您可以使用 StringToJSON 中生成 Value,然后在 FromJSON.

中解析时匹配 String 的特定形状
instance ToJSON GoalStatus where
  toJSON (Created) = String "created"
  toJSON (InProgress) = String "inProgress"
  toJSON (Accomplished) = String "accomplished"
  toJSON (GivenUp) = String "givenUp"

instance FromJSON GoalStatus  where
  parseJSON (String s) = case unpack s of
    "created" -> return Created
    "inProgress" -> return InProgress
    "accomplished" -> return Accomplished
    "givenUp" -> return GivenUp
    _ -> error $ "Json format not expected"
  parseJSON _ =  error $ "Json format not expected"

我不确定我是否理解问题。这是一个包含通用派生实现的完整文件:

{-# LANGUAGE DeriveGeneric #-}
module Q54178405 where

import Data.Text
import Data.Aeson
import GHC.Generics

type WorkspaceId = Int
type GoalId = Int
type GoalDescription = Text

data GoalStatus =
  Created | Accomplished | InProgress | GivenUp deriving (Show, Eq, Generic)

instance ToJSON GoalStatus
instance FromJSON GoalStatus

data Goal = Goal {
    workspaceId ::WorkspaceId
  , goalId :: GoalId
  , description :: GoalDescription
  , status :: GoalStatus}
  deriving (Show, Eq, Generic)

instance ToJSON Goal
instance FromJSON Goal

以下是它在 GHCi 中的表现:

*Q54178405 Q54178405> encode $ Goal 42 1337 "foo" Accomplished
"{\"status\":\"Accomplished\",\"goalId\":1337,\"workspaceId\":42,\"description\":\"foo\"}"
*Q54178405 Q54178405> encode $ Goal 42 1337 "foo" GivenUp
"{\"status\":\"GivenUp\",\"goalId\":1337,\"workspaceId\":42,\"description\":\"foo\"}"

这不就是你想要的吗?

实例往返也是:

*Q54178405 Q54178405> decode $ encode $ Goal 42 1337 "foo" GivenUp :: Maybe Goal
Just (Goal {workspaceId = 42, goalId = 1337, description = "foo", status = GivenUp})

如果这不是您想要的,它对于一些具有所需输出的输入的明确示例会很有用。