2016-01-20 4 views
0

У меня есть обработчик маршрута Есод, который возвращает JSON с объектомДобавить вложенное свойство возвращаемой записи

{ id: 1, 
    title: "foo" 
    content: "bar" 
} 

Я хотел бы добавить _links свойство с некоторыми метаданными, которые не существуют в на Entity себя, например

{ id: 1, 
    title: "foo" 
    content: "bar" 
    _links: {self: http://localhost:3000/events/1} 
} 

Как я могу добавить _links к существующей записи Entity? Вот мой обработчик:

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 

ответ

2

Для этого вам придется конвертировать ваши 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, чтобы получить новый Value с ключом "_links". Я использовал Maybe здесь для защиты от случая, когда toJSON :: Entity -> Value не возвращает Value с помощью конструктора Object. Это защитит вас в будущем, если вы измените тип Entity и как он преобразуется в JSON, но забудьте обновить всю свою кодовую базу, чтобы отразить это изменение.

EDIT: Если вы должны были использовать lens-aeson Вы могли бы написать это так, хотя это требует изменения порядка аргументов только для опрятности:

buildEntityWithLink :: Text -> Entity -> Value 
buildEntityWithLink renderedUrl = (
    over _Object $ 
     HashMap.insert "_links" $ 
         object ["self" .= renderedUrl] 
    ) . toJSON 

Это фактически позволяет отказаться от Maybe, вы хотите, чтобы в любом случае, так как способ работы линз означает, что если Object не является верхним уровнем, тогда возвращается исходное значение, поэтому buildEntityWithLink "testlink" ([] :: [Entity]) будет просто возвращать то же самое, что и toJSON ([] :: [Entity]), а именно пустой Array. toJSON должен находиться снаружи от объектива, потому что для составления с _Object он должен быть в качестве сеттера, и toJSON не может быть легко превращен в сеттер. Вместо этого мы просто предварительно обработаем наш Entity в Value, а затем подайте его в выражение объектива. Я добавил пробелы, где я выровнял аргументы для каждой функции, делает ее более читаемой ИМО, но это все технически 1 строка кода. Полезной особенностью этой реализации является то, что теперь легко отключить сигнатуру типа до ToJSON a => Text -> a -> Value, поэтому вы можете добавить _links в любой тип, который вы хотите.

+0

Спасибо за это. «Использование пакета, совместимого с объективом для aeson, возможно, упростило бы это совсем немного», - у вас есть пример для этого, я не мог найти? – amitaibu

+1

@amitaibu Проверить мои изменения – bheklilr

0

на основе bheklilr'sработают комментария - вот окончательный код теперь у меня есть:

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 ссылкой

Смежные вопросы