问题
Toying a bit with the github gist API while trying to get down with the Aeson JSON library. I've run into a problem with the generated ToJSON instance, and I don't know exactly how to solve it.
I need to contain a value inside and the key that is associated to the value also needs to be a value and not a predefined key name. It's a bit easier to show. The desired output is,
{
"public": true,
"description": "Something..",
"files": {"This Thing.md": {"content": "Here we go!"}}
}
where the value of the filename is holding the content, but currently I get,
{
"public": true,
"description": "Something..",
"files": {"filename": "This Thing.md", "content": "Here we go!"}
}
Which isn't really what I need. The current code is,
{-# 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
Under the assumption that it is possible, how would my datastructure need to look to get the desired output?.. And if that's not possible using the generics, how'd I got about it using the ToJSON instance (I can't quite figure out the structure there either)?
回答1:
Your problem stems from an incorrect schema. files
can currently only contain one GistContent
, which is unnecessarily limiting. Instead, you'd want to have a list of GistContent
s:
data Gist = Gist
{ description :: Text
, public :: Bool
, files :: [GistContent]
} deriving (Show, Generic)
Now consider another constraint on Gist
: each GistContent
must have a different filename
. A data structure that would enforce this would be Data.HashMap.Strict.HashMap. Taking the filename
out of GistContent
and using the filename as a key:
data GistContent = GistContent
{ content :: Text
} deriving (Show, Generic)
data Gist = Gist
{ description :: Text
, public :: Bool
, files :: HashMap Text GistContent
} deriving (Show, Generic)
Everything works out.
回答2:
Here's the manually written instance (see the documentation for the class):
instance ToJSON GistContent where
toJSON (GistContent { filename = f, content = c }) = object [f .= c]
I doubt if there would be any way to get this with your existing datatype with the automatically generated instances because all they can do is to follow the datatype using a standard scheme. Note that you can still use the generic instance for Gist
because that will call the (non-generic) instance for GistContent
.
来源:https://stackoverflow.com/questions/21358563/using-aeson-generics-to-construct-json-with-a-value-as-key-holding-another-value