将嵌套属性添加到返回的记录 [英] Add nested property to returned record

查看:142
本文介绍了将嵌套属性添加到返回的记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个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键,然后再次创建一个

 <$ c     aeson  $ 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆