我有几种类型,User
和Post
。 Post
由User
创建。{/ p>
我的数据库与我的类型相同,即
data User = { userID :: Integer, name :: String }
data Post = { content :: String, authorID :: Integer } -- authorID is the userID
假设我有基于这些类型的有效To / FromJSON实例,我将如何有条件地在我的JSON输出中包含一个值?
例如,我可能希望查看用户“个人资料”,其中包含Post
生成的所有User
。其他时候,我可能想要搜索系统中的各种User
,因此返回他们所做的Post
毫无意义。
我在检索给定Post
的{{1}}时没有问题,检索给定User
的{{1}}时也没有问题。我只想知道有条件地为我的JSON添加密钥的最佳方法。
有人向我提到我应该为User
Post
添加一个字段,并为User
类型添加posts :: Maybe [Post]
。虽然这适用于相当小的类型,但我觉得它会使某些类型的关系很大并且可能难以维护。
我还想过使用author :: Maybe User
来构建/序列化我的信息,但是通过做类似的事情,你失去了大多数(全部?)类型的安全性。
修改
根据上面的数据类型,我定义了以下ToJSON实例。
Post
这会给我“简单”的json输出,类似于Map Text Data.Aeson.Value
:
instance ToJSON User where
toJSON (User i n) = object [ "id" .= i, "name" .= n ]
instance ToJSON Post where
toJSON (Post c a) = object [ "content" .= c, "author_id" .= a ]
User
:
{
"id": 4,
"name": "User Name"
}
这些可能很有用,尤其是在搜索项目时。这样只允许显示有限数量的信息。
我可以按照
的方式做点什么Post
假设有{
"content": "This is the content of my post",
"author_id": 4
}
的有效定义,那将向我提供用户信息以及与该用户关联的所有帖子。
这会给我JSON类似于
instance ToJSON User where
toJSON (User i n) = object [ "id" .= i, "name" .= n, "posts" .= posts i ]
我不明白我将如何动态设置我想发送给客户端的字段。是否可以定义ToJSON的多个实例并选择我想用于给定请求的实例?或者我应该只执行之前建议的内容,并在posts
类型中添加{
"id": 4,
"name": "User Name",
"posts": [
{
"content": "This is some content",
"author_id": 4
}
]
}
字段,该字段可以包含posts
?
答案 0 :(得分:4)
我不认为您是否应该使用动态JSON或坚持修复ToJSON
实例,但您可以轻松地将两者混合并将额外字段添加到JSON值是从您的实例生成的。
根据您所写的内容,我猜您知道Aeson将对象表示为Map Text Value
?对于数组,它使用Vector Value
。
因此,您可以在toJSON
和User
列表中调用Posts
,如果需要,只需在地图中插入帖子数组作为附加条目:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Aeson
import Data.Aeson.Types
import qualified Data.HashMap.Strict as M
data User = User { userID :: Integer, name :: String }
data Post = Post { content :: String, authorID :: Integer }
instance ToJSON User where
toJSON (User i n) = object [ "id" .= i, "name" .= n ]
instance ToJSON Post where
toJSON (Post c a) = object [ "content" .= c, "author_id" .= a ]
babs = User { userID = 5, name = "babs" }
posts = [ Post { content = "blah", authorID = 5 } ]
userWithPosts = Object (M.insert "posts" v o)
where
Object o = toJSON babs
v = toJSON posts
main = B.putStrLn (encode userWithPosts)
encode
调用为您提供了组合JSON:
*Main B> main
{"name":"babs","id":5,"posts":[{"author_id":5,"content":"blah"}]}