使用Aeson泛型构造JSON,其值为key,保存另一个值

时间:2014-01-26 01:37:12

标签: haskell aeson

尝试使用Aeson JSON库时,使用github gist API进行了一些操作。我遇到了生成的ToJSON实例的问题,我不确切知道如何解决它。

我需要在里面包含一个值,并且与该值相关联的键也需要是一个值而不是一个预定义的键名。展示会更容易一些。所需的输出是,

{
    "public": true, 
    "description": "Something..", 
    "files": {"This Thing.md": {"content": "Here we go!"}}
}

其中文件名的值保存内容,但目前我得到了,

{
    "public": true, 
    "description": "Something..", 
    "files": {"filename": "This Thing.md", "content": "Here we go!"}
}

这不是我真正需要的。目前的代码是,

{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
import Data.Text (Text)
import Data.Aeson
import GHC.Generics

data GistContent = GistContent
    { filename :: Text
    , content :: Text
      } deriving (Show, Generic)

instance ToJSON GistContent

data Gist = Gist
    { description :: Text
    , public      :: Bool
    , files       :: GistContent
      } deriving (Show, Generic)

instance ToJSON Gist

假设有可能,我的数据结构如何才能获得所需的输出?如果使用泛型不可能,我怎么用ToJSON实例得到它(我可以'那里的结构很明显?)

2 个答案:

答案 0 :(得分:3)

这是手动编写的实例(参见documentation for the class):

instance ToJSON GistContent where
   toJSON (GistContent { filename = f, content = c }) = object [f .= c]

我怀疑是否有任何方法可以使用自动生成的实例来获取现有数据类型,因为他们所能做的就是使用标准方案来遵循数据类型。请注意,您仍然可以使用Gist的通用实例,因为它会调用GistContent的(非通用)实例。

答案 1 :(得分:3)

您的问题源于错误的架构。 files目前只能包含一个GistContent,这是不必要的限制。相反,您需要一个GistContent s列表:

data Gist = Gist
    { description :: Text
    , public      :: Bool
    , files       :: [GistContent]
    } deriving (Show, Generic)

现在考虑对Gist的另一个约束:每个GistContent必须有不同的filename。强制执行此操作的数据结构为Data.HashMap.Strict.HashMap。从filename中取出GistContent并使用文件名作为键:

data GistContent = GistContent
    { content :: Text
    } deriving (Show, Generic)

data Gist = Gist
    { description :: Text
    , public      :: Bool
    , files       :: HashMap Text GistContent
    } deriving (Show, Generic)

一切顺利。