添加嵌套 属性 到返回的记录
Add nested property to returned record
我有一个 Yesod 路由处理程序,它 returns 一个 JSON 对象
{ id: 1,
title: "foo"
content: "bar"
}
我想添加一个 _links
属性 和一些实体本身不存在的元数据,例如
{ id: 1,
title: "foo"
content: "bar"
_links: {self: http://localhost:3000/events/1}
}
如何将 _links
添加到现有的实体记录中?这是我的处理程序:
getEventR :: EventId -> Handler Value
getEventR eid = do
event <- runDB $ get404 eid
render <- getUrlRender
let renderedUrl = render $ EventR eid
let links = object
[ "self" .= renderedUrl
]
let returnVal = object
[ "data" .= (Entity eid event)
, "_links" .= links
]
return returnVal
为此,您必须手动将 Entity
转换为 Value
,然后使用 unordered-containers:Data.HashMap.Strict
中的函数插入 "_links"
键,然后再次从中构建一个 Value
。不过,为 aeson
使用镜头兼容包可能会大大简化这一过程:
buildEntityWithLink :: Entity -> Text -> Maybe Value
buildEntityWithLink entity renderedUrl = case toJSON entity of
Object obj ->
let links = object ["self" .= renderedUrl]
entityWithLink = HashMap.insert "_links" links obj
in Just (Object entityWithLink)
_ -> Nothing
(我假设 renderedUrl
的类型为 Text
,如果需要可以更改)
然后您只需传入 Entity
和 renderedUrl
即可获得包含 "_links"
密钥的新 Value
。我在这里使用 Maybe
来防止 toJSON :: Entity -> Value
不使用 Object
构造函数 return 和 Value
的情况。如果您更改 Entity
类型及其转换为 JSON 的方式,但忘记更新所有代码库以反映此更改,这将在将来保护您。
编辑:如果你要使用 lens-aeson
你可以这样写,尽管这需要为了整洁而交换参数顺序:
buildEntityWithLink :: Text -> Entity -> Value
buildEntityWithLink renderedUrl = (
over _Object $
HashMap.insert "_links" $
object ["self" .= renderedUrl]
) . toJSON
这实际上可以让您删除 Maybe
,无论如何,因为镜头的工作方式意味着如果 Object
不是最高级别,那么原始值是 returned,所以 buildEntityWithLink "testlink" ([] :: [Entity])
将 return 与 toJSON ([] :: [Entity])
相同,即空 Array
。 toJSON
必须在镜头操作的外部,因为为了与 _Object
组合,它必须能够成为 setter,而 toJSON
不能很容易做成setter。相反,我们只是 pre-process 我们的 Entity
变成 Value
,然后将其输入到镜头表达式中。我在排列每个函数的参数的地方添加了空格,使它在我看来更具可读性,但这在技术上都是一行代码。此实现的一个有用特性是,现在可以轻松地将类型签名放宽为 ToJSON a => Text -> a -> Value
,因此您可以将 _links
添加到您想要的任何类型。
基于 working 评论 - 这是我现在的最终代码:
import Data.HashMap.Strict as HashMap (insert)
getEventR :: EventId -> Handler Value
getEventR eid = do
event <- runDB $ get404 eid
render <- getUrlRender
let renderedUrl = render $ EventR eid
let returnVal = object
[ "data" .= [buildEntityWithLink (Entity eid event) renderedUrl]]
return returnVal
buildEntityWithLink :: Entity Event -> Text -> Maybe Value
buildEntityWithLink entity renderedUrl =
case toJSON entity of
Object obj ->
let links = object ["self" .= renderedUrl]
entityWithLink = HashMap.insert "_links" links obj
in Just (Object entityWithLink)
_ -> Nothing
现在确实,JSON 与 self
link
一起出现
我有一个 Yesod 路由处理程序,它 returns 一个 JSON 对象
{ id: 1,
title: "foo"
content: "bar"
}
我想添加一个 _links
属性 和一些实体本身不存在的元数据,例如
{ id: 1,
title: "foo"
content: "bar"
_links: {self: http://localhost:3000/events/1}
}
如何将 _links
添加到现有的实体记录中?这是我的处理程序:
getEventR :: EventId -> Handler Value
getEventR eid = do
event <- runDB $ get404 eid
render <- getUrlRender
let renderedUrl = render $ EventR eid
let links = object
[ "self" .= renderedUrl
]
let returnVal = object
[ "data" .= (Entity eid event)
, "_links" .= links
]
return returnVal
为此,您必须手动将 Entity
转换为 Value
,然后使用 unordered-containers:Data.HashMap.Strict
中的函数插入 "_links"
键,然后再次从中构建一个 Value
。不过,为 aeson
使用镜头兼容包可能会大大简化这一过程:
buildEntityWithLink :: Entity -> Text -> Maybe Value
buildEntityWithLink entity renderedUrl = case toJSON entity of
Object obj ->
let links = object ["self" .= renderedUrl]
entityWithLink = HashMap.insert "_links" links obj
in Just (Object entityWithLink)
_ -> Nothing
(我假设 renderedUrl
的类型为 Text
,如果需要可以更改)
然后您只需传入 Entity
和 renderedUrl
即可获得包含 "_links"
密钥的新 Value
。我在这里使用 Maybe
来防止 toJSON :: Entity -> Value
不使用 Object
构造函数 return 和 Value
的情况。如果您更改 Entity
类型及其转换为 JSON 的方式,但忘记更新所有代码库以反映此更改,这将在将来保护您。
编辑:如果你要使用 lens-aeson
你可以这样写,尽管这需要为了整洁而交换参数顺序:
buildEntityWithLink :: Text -> Entity -> Value
buildEntityWithLink renderedUrl = (
over _Object $
HashMap.insert "_links" $
object ["self" .= renderedUrl]
) . toJSON
这实际上可以让您删除 Maybe
,无论如何,因为镜头的工作方式意味着如果 Object
不是最高级别,那么原始值是 returned,所以 buildEntityWithLink "testlink" ([] :: [Entity])
将 return 与 toJSON ([] :: [Entity])
相同,即空 Array
。 toJSON
必须在镜头操作的外部,因为为了与 _Object
组合,它必须能够成为 setter,而 toJSON
不能很容易做成setter。相反,我们只是 pre-process 我们的 Entity
变成 Value
,然后将其输入到镜头表达式中。我在排列每个函数的参数的地方添加了空格,使它在我看来更具可读性,但这在技术上都是一行代码。此实现的一个有用特性是,现在可以轻松地将类型签名放宽为 ToJSON a => Text -> a -> Value
,因此您可以将 _links
添加到您想要的任何类型。
基于
import Data.HashMap.Strict as HashMap (insert)
getEventR :: EventId -> Handler Value
getEventR eid = do
event <- runDB $ get404 eid
render <- getUrlRender
let renderedUrl = render $ EventR eid
let returnVal = object
[ "data" .= [buildEntityWithLink (Entity eid event) renderedUrl]]
return returnVal
buildEntityWithLink :: Entity Event -> Text -> Maybe Value
buildEntityWithLink entity renderedUrl =
case toJSON entity of
Object obj ->
let links = object ["self" .= renderedUrl]
entityWithLink = HashMap.insert "_links" links obj
in Just (Object entityWithLink)
_ -> Nothing
现在确实,JSON 与 self
link