有条件地将字段添加到 JSON 输出
Conditionally add fields to JSON output
我有几个类型,User
和 Post
。 Post
是由 User
创建的。
我的数据库看起来和我的类型一样,都是
data User = { userID :: Integer, name :: String }
data Post = { content :: String, authorID :: Integer } -- authorID is the userID
假设我有基于这些类型的有效 To/FromJSON 实例,我将如何有条件地在我的 JSON 的输出中包含一个值?
例如,我可能想查看一个用户 "profile",其中包括 User
创建的所有 Post
。其他时候,我可能想在系统中搜索各种 User
,因此返回他们制作的 Post
没有意义。
检索给定 User
的 Post
没有问题,检索给定 Post
的 User
也没有问题。我只想知道有条件地向我的 JSON.
添加密钥的最佳方法
有人向我提到我应该在 User
中为 posts :: Maybe [Post]
添加一个字段,并在我的 Post
类型中添加一个 author :: Maybe User
。虽然这适用于相当小的类型,但我认为它会使具有很多关系的某些类型变得非常大并且可能难以维护。
我也曾考虑过使用 Map Text Data.Aeson.Value
来构建/序列化我的信息,但这样做会失去大部分(全部?)类型安全性。
编辑
根据上面的数据类型,我定义了以下 ToJSON 个实例。
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 ]
这会给我 "simple" json 输出,类似于 User
:
{
"id": 4,
"name": "User Name"
}
Post
:
{
"content": "This is the content of my post",
"author_id": 4
}
这些可能很有用,尤其是在搜索项目时。这允许只显示有限数量的信息。
我可以按照
做一些事情
instance ToJSON User where
toJSON (User i n) = object [ "id" .= i, "name" .= n, "posts" .= posts i ]
这会给我用户信息,以及与该用户相关的所有帖子,假设 posts
的定义有效。
这会让我 JSON 类似于
{
"id": 4,
"name": "User Name",
"posts": [
{
"content": "This is some content",
"author_id": 4
}
]
}
我不明白如何动态设置要发送给客户端的字段。是否可以定义 ToJSON 的多个实例并选择我想用于给定请求的实例?或者我应该按照之前的建议做,并在我的 User
类型中包含一个 posts
字段,该字段可以容纳 Maybe [Post]
?
对于应该使用动态 JSON 还是坚持使用固定 ToJSON
实例,我认为没有一个通用的答案,但您可以轻松地将两者混合并向 JSON 从您的实例生成的值。
根据您所写的内容,我猜您知道 Aeson 将对象表示为 Map Text Value
?对于数组,它使用 Vector Value
.
因此,您可以在 User
和 Posts
的列表中调用 toJSON
,如果您需要,只需将 post 数组作为附加条目插入地图想要它:
{-# 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"}]}
我有几个类型,User
和 Post
。 Post
是由 User
创建的。
我的数据库看起来和我的类型一样,都是
data User = { userID :: Integer, name :: String }
data Post = { content :: String, authorID :: Integer } -- authorID is the userID
假设我有基于这些类型的有效 To/FromJSON 实例,我将如何有条件地在我的 JSON 的输出中包含一个值?
例如,我可能想查看一个用户 "profile",其中包括 User
创建的所有 Post
。其他时候,我可能想在系统中搜索各种 User
,因此返回他们制作的 Post
没有意义。
检索给定 User
的 Post
没有问题,检索给定 Post
的 User
也没有问题。我只想知道有条件地向我的 JSON.
有人向我提到我应该在 User
中为 posts :: Maybe [Post]
添加一个字段,并在我的 Post
类型中添加一个 author :: Maybe User
。虽然这适用于相当小的类型,但我认为它会使具有很多关系的某些类型变得非常大并且可能难以维护。
我也曾考虑过使用 Map Text Data.Aeson.Value
来构建/序列化我的信息,但这样做会失去大部分(全部?)类型安全性。
编辑
根据上面的数据类型,我定义了以下 ToJSON 个实例。
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 ]
这会给我 "simple" json 输出,类似于 User
:
{
"id": 4,
"name": "User Name"
}
Post
:
{
"content": "This is the content of my post",
"author_id": 4
}
这些可能很有用,尤其是在搜索项目时。这允许只显示有限数量的信息。
我可以按照
做一些事情instance ToJSON User where
toJSON (User i n) = object [ "id" .= i, "name" .= n, "posts" .= posts i ]
这会给我用户信息,以及与该用户相关的所有帖子,假设 posts
的定义有效。
这会让我 JSON 类似于
{
"id": 4,
"name": "User Name",
"posts": [
{
"content": "This is some content",
"author_id": 4
}
]
}
我不明白如何动态设置要发送给客户端的字段。是否可以定义 ToJSON 的多个实例并选择我想用于给定请求的实例?或者我应该按照之前的建议做,并在我的 User
类型中包含一个 posts
字段,该字段可以容纳 Maybe [Post]
?
对于应该使用动态 JSON 还是坚持使用固定 ToJSON
实例,我认为没有一个通用的答案,但您可以轻松地将两者混合并向 JSON 从您的实例生成的值。
根据您所写的内容,我猜您知道 Aeson 将对象表示为 Map Text Value
?对于数组,它使用 Vector Value
.
因此,您可以在 User
和 Posts
的列表中调用 toJSON
,如果您需要,只需将 post 数组作为附加条目插入地图想要它:
{-# 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"}]}