将嵌套属性添加到返回的记录 [英] Add nested property to returned record
问题描述
我有一个Yesod路由处理程序,该对象返回一个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
/ code>,然后使用 unordered-containers:Data.HashMap.Strict
中的函数插入_ links
键,然后再次创建一个值
。
<$ caeson $ c> buildEntityWithLink :: Entity - >文字 - >也许价值
buildEntityWithLink实体renderedUrl = case toJSON实体
Object obj - >
let links = object [self。= renderedUrl]
entityWithLink = HashMap.insert_links链接obj
在Just(Object entityWithLink)
_ - > Nothing
(我假设 renderedUrl
有类型 Text
,如果需要则更改)
Entity 和 renderedUrl
通过_链接获得新的值
包括
键。我在这里使用了 Maybe
来防止 toJSON :: Entity - >值
不会使用 Object
构造函数返回 Value
。如果您更改 Entity
类型以及它如何转换为JSON,但忘记更新所有代码库以反映此更改,这将在将来保护您。
编辑:如果你要使用 lens-aeson
你可以这样写,尽管这需要交换参数顺序以保持整洁:
buildEntityWithLink :: Text - >实体 - >值
buildEntityWithLink renderedUrl =(
超过_Object $
HashMap.insert_links$
object [self。= renderedUrl]
)。 toJSON
实际上,您可以放弃也许
,无论如何,因为镜头的工作方式意味着如果 Object
不是顶级,那么返回原始值,所以 buildEntityWithLink testlink([] :: [Entity])
只会返回与相同的JSON([] :: [Entity])
,即一个空的 Array
。 toJSON
必须位于镜头操作的外部,因为要与 _Object
组合,必须能够成为一个setter,并且 toJSON
不能轻易地成为setter。相反,我们只是将我们的实体
预处理为值
,然后将其送入镜头表达式。我在每个函数的参数列表中添加了空格,使其更具可读性,但这在技术上只有1行代码。关于这个实现的一个有用的特性是现在很容易将类型签名放宽到 ToJSON a =>文字 - > a - >值
,所以你可以添加 _links
到任何你想要的类型。
I have a Yesod route handler that returns a JSON with the object
{ id: 1,
title: "foo"
content: "bar"
}
I would like to add a _links
property with some metadata, that doesn't exist in on the Entity itself, e.g.
{ id: 1,
title: "foo"
content: "bar"
_links: {self: http://localhost:3000/events/1}
}
How can I add the _links
to the existing Entity record? Here's my handler:
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
解决方案 To do this you'll have to convert your Entity
to a Value
manually, then use the functions in unordered-containers:Data.HashMap.Strict
to insert the "_links"
key, then build a Value
out of it again. Using the lens compatible package for aeson
could probably simplify this quite a bit though:
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
(I'm assuming that renderedUrl
has type Text
, change if needed)
Then you can just pass in your Entity
and renderedUrl
to get a new Value
with the "_links"
key included. I've used Maybe
here to guard against the case where toJSON :: Entity -> Value
does not return a Value
with the Object
constructor. This will protect you in the future if you change the Entity
type and how it converts to JSON but forget to update all of your codebase to reflect this change.
EDIT: If you were to use lens-aeson
You could write it like this, though this requires swapping the argument order just for neatness:
buildEntityWithLink :: Text -> Entity -> Value
buildEntityWithLink renderedUrl = (
over _Object $
HashMap.insert "_links" $
object ["self" .= renderedUrl]
) . toJSON
This actually lets you drop the Maybe
, you want to anyway, since the way lenses work means that if the Object
isn't the top level then the original value is returned, so buildEntityWithLink "testlink" ([] :: [Entity])
Would just return the same as toJSON ([] :: [Entity])
, namely an empty Array
. The toJSON
has to be on the outside of the lens operation because in order to compose with _Object
it has to be able to be a setter, and toJSON
can't be easily made into a setter. Instead, we just pre-process our Entity
into a Value
, then feed it in to the lens expression. I've added whitespace where I've lined up the arguments to each function, makes it a bit more readable IMO, but this is all technically 1 line of code. A useful feature about this implementation is that it's now easy to relax the type signature to ToJSON a => Text -> a -> Value
, so you can add a _links
to any type you want to.
这篇关于将嵌套属性添加到返回的记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!