2

在尝试使用 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 实例解决的(我不太清楚那里的结构也没有)?

4

2 回答 2

3

这是手动编写的实例(请参阅该类的文档):

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

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

于 2014-01-26T01:50:07.133 回答
3

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

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

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

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

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

一切正常。

于 2014-01-26T01:51:19.920 回答