haskell - 我可以在多大程度上让 Aeson 承担这项繁重的工作?

标签 haskell jira aeson

我试图避免编写 toJSON 的定义。这是我遇到的错误:

Datatypes.hs:92:10:
    No instance for (aeson-0.6.0.2:Data.Aeson.Types.Class.GToJSON
                       (GHC.Generics.Rep (HashMap Key Project)))
      arising from a use of `aeson-0.6.0.2:Data.Aeson.Types.Class.$gdmtoJSON'
Possible fix:
  add an instance declaration for
  (aeson-0.6.0.2:Data.Aeson.Types.Class.GToJSON
     (GHC.Generics.Rep (HashMap Key Project)))
In the expression:
  (aeson-0.6.0.2:Data.Aeson.Types.Class.$gdmtoJSON)
In an equation for `toJSON':
    toJSON = (aeson-0.6.0.2:Data.Aeson.Types.Class.$gdmtoJSON)
In the instance declaration for `ToJSON (HashMap Key Project)'

我的所有 HashMap data 声明都遇到类似的错误。

这是相关代码。如果缺少信息,请告诉我。

{-# LANGUAGE DeriveGeneric #-}  -- for JTask and Fields ToJSON instances:w!
{-# LANGUAGE DeriveDataTypeable #-} -- This may be needed for HashMaps
{-# LANGUAGE FlexibleInstances #-} -- for the HashMap ToJSON instances
{-# LANGUAGE DefaultSignatures #-}

import Prelude
import Data.ByteString
import GHC.Generics (Generic )
import Data.Data 
import Data.Typeable (Typeable) -- fix HashMap ToJSON instances? maybe
import Data.Aeson
import Data.Aeson.Generic  
import Data.Aeson.Types -- (ToJSON,FromJSON)
import Data.HashMap.Strict (HashMap)

data JTask = JTask {fields :: Fields} deriving (Typeable,Data,Generic)
data Fields = Fields { project :: HashMap Key Project
                     , summary :: ByteString
                     , issuetype :: HashMap Name Task
                     , versions :: [HashMap Name Version]
                     , description :: ByteString
                     } deriving (Typeable,Data,Generic)

data Key = Key deriving (Typeable,Data,Generic)
instance Show Key where
   show Key = "key"

data Name = Name deriving (Typeable,Data,Generic)
instance Show Name where
   show Name = "name"

data Task = Task deriving (Typeable,Data,Generic)

type Version = ByteString -- Placeholder type. Probably using Day for realsies.

data Project = BNAP deriving (Typeable,Data,Generic) -- Fill this out as we go 

instance Generic (HashMap Key Project)
instance Data (HashMap Key Project)
--instance GToJSON (HashMap Key Project)

instance Generic (HashMap Name ByteString)
instance Data (HashMap Name ByteString)

instance Generic (HashMap Name Task)
instance Data (HashMap Name Task)
-- JSON instances
instance ToJSON CreateError
instance ToJSON Fields
instance ToJSON JTask 

instance ToJSON Key
instance ToJSON Name
instance ToJSON Task
instance ToJSON Project

instance ToJSON (HashMap Key Project)
instance ToJSON (HashMap Name Task)
instance ToJSON (HashMap Name ByteString)
-- instance ToJSON Version uncomment when we change Version's type.

我无法为 Data.Aeson.Types.Class.GToJSON 创建实例,因为 Data.Aeson.Types.Class 未导出。我有什么选择?我必须手动写什么? deriveJSON 是最佳选择吗?

更新: 我实现了下面的建议。 这是代码

createObject :: CreateConf -> ResourceT IO Value
createObject (CreateConf iSummary iDesc dd) = do
   let jfields = Fields {project = singleton Key BNAP
                        ,summary = iSummary
                        ,issuetype = singleton Name Task
                        ,versions = [singleton Name (calcVersion dd)]
                        ,description = iDesc
                        }
return $ toJSON (JTask jfields)

第一个实例产生 Object fromList [("key",Array (fromList []))])

第二个实例产生Object fromList [("name",Array (fromList []))]

知道为什么 namekey 为空吗?

我怎样才能知道?

仅使用 deriveJSON 会更容易吗?

更新: 感谢 NathanHowell 的帮助,更重要的问题已经解决,即一元类型的 GToJSON 实例。解决方案是为一元类型创建我自己的实例。 JSON 对象乱序,但我不知道这是否重要。如果是这样,Fields 的另一个手动 ToJSON 实例似乎可以解决这个问题。

更新: 好的,有一点背景。我正在编写 JIRA 前端。我提到这一点是因为将来的人们可能会来这里发现以下好消息:JIRA 不关心对象顺序。

最佳答案

Aeson 为 HashMap String a 提供了一个 ToJSON 实例。让事情正常工作的最简单方法是将 HashMap 键转换为 Strings 来使用此实例。只需一些样板文件,它就使用 Generic 实例来处理其他所有内容。

{-# LANGUAGE DeriveGeneric #-}  -- for JTask and Fields ToJSON instances:w!
{-# LANGUAGE FlexibleInstances #-} -- for the HashMap ToJSON instances
{-# LANGUAGE DefaultSignatures #-}

import Prelude
import Data.ByteString
import GHC.Generics (Generic )
import Data.Aeson
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap

data JTask = JTask {fields :: Fields} deriving (Generic)
data Fields = Fields { project :: HashMap Key Project
                     , summary :: ByteString
                     , issuetype :: HashMap Name Task
                     , versions :: [HashMap Name Version]
                     , description :: ByteString
                     } deriving (Generic)

data Key = Key deriving (Generic)
instance Show Key where
   show Key = "key"

data Name = Name deriving (Generic)
instance Show Name where
   show Name = "name"

data Task = Task deriving (Generic)

type Version = ByteString -- Placeholder type. Probably using Day for realsies.

data Project = BNAP deriving (Generic) -- Fill this out as we go 

instance ToJSON Fields
instance ToJSON JTask 

instance ToJSON Key
instance ToJSON Name
instance ToJSON Task
instance ToJSON Project

mapfst :: (a -> b) -> [(a, v)] -> [(b, v)]
mapfst f = fmap $ \ (k, v) -> (f k, v)

instance ToJSON a => ToJSON (HashMap Key a) where
  toJSON = toJSON . HashMap.fromList . mapfst show . HashMap.toList

instance ToJSON a => ToJSON (HashMap Name a) where
  toJSON = toJSON . HashMap.fromList . mapfst show . HashMap.toList

关于haskell - 我可以在多大程度上让 Aeson 承担这项繁重的工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13368150/

相关文章:

haskell - 如何将Stackage安装为系统默认值?

python - 如何避免在 pytest_jira 插件的 jira.cfg 中硬编码用户名/密码

Haskell 函数检查数字是否为奇数,而不使用 odd 函数

haskell - Haskell 对函数定义中的顺序敏感吗?

haskell - 单子(monad)绑定(bind)的显式签名约束

sql - JIRA查询没有 parent 的问题

maven - 无法启动插件 : Unable to resolve 167. 0 : missing requirement [167. 0] osgi.wiring.package; (osgi.wiring.package=com.atlassian.inject)

json - 在 Haskell 中解析解码后的 JSON

haskell - Aeson:将动态键解析为类型字段

Haskell Aeson 解构泛型解析