https://github.com/ncrashed/aeson-injector
Haskell library for injecting fields into aeson values
https://github.com/ncrashed/aeson-injector
Last synced: 12 months ago
JSON representation
Haskell library for injecting fields into aeson values
- Host: GitHub
- URL: https://github.com/ncrashed/aeson-injector
- Owner: NCrashed
- License: mit
- Created: 2016-07-26T17:56:55.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2023-01-09T19:51:59.000Z (over 3 years ago)
- Last Synced: 2025-06-10T04:08:30.924Z (12 months ago)
- Language: Haskell
- Size: 73.2 KB
- Stars: 13
- Watchers: 4
- Forks: 8
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# aeson-injector
[](https://travis-ci.org/NCrashed/aeson-injector)
It is small utility library that is intented to be used in RESTful APIs, especially with [servant](http://haskell-servant.readthedocs.io/en/stable/) and [Swagger](http://swagger.io/). Its main purpose is simple injection of fields into JSONs produced by [aeson](https://hackage.haskell.org/package/aeson) library.
Consider the following common data type in web service developing:
``` haskell
data News = News {
title :: Text
, body :: Text
, author :: Text
, timestamp :: UTCTime
}
-- Consider we have simple 'ToJSON' and 'FromJSON' instances
$(deriveJSON defaultOptions ''News)
```
[ToJSON](http://hackage.haskell.org/package/aeson-0.11.2.0/docs/Data-Aeson.html#t:ToJSON) instance produces JSON's like:
``` json
{
"title": "Awesome piece of news!"
, "body": "Big chunk of text"
, "author": "Just Me"
, "timestamp": "2016-07-26T18:54:42.678999Z"
}
```
Now one can create a simple web server with servant DSL:
``` haskell
type NewsId = Word
type NewsAPI =
ReqBody '[JSON] News :> Post '[JSON] NewsId
:<|> Capture "news-id" NewsId :> Get '[JSON] News
:<|> "list" :> Get '[JSON] [News]
```
All seems legit, but, wait a second, an API user definitely would like to know id of news in `list` method. One way to do this is declare new type `NewsInfo` with additional field, but it is bad solution as requires to keep extra data type for each resource.
So, here `aeson-injector` steps in, now you can write:
``` haskell
type NewsAPI =
ReqBody '[JSON] News :> Post '[JSON] NewsId
:<|> Capture "news-id" NewsId :> Get '[JSON] News
:<|> "list" :> Get '[JSON] [WithField "id" NewsId News]
```
`WithField "id" NewsId News` or simply `WithId NewsId News` wraps you data type and injects `id` field in produced JSON values:
``` haskell
>>> encode (WithField 42 myNews :: WithField "id" NewsId News)
```
``` json
{
"id": 42
, "title": "Awesome piece of news!"
, "body": "Big chunk of text"
, "author": "Just Me"
, "timestamp": "2016-07-26T18:54:42.678999Z"
}
```
`WithField` data type has `FromJSON` instance for seamless parsing of data with injected fields and [ToSchema](https://hackage.haskell.org/package/swagger2-2.1/docs/Data-Swagger-Internal-Schema.html#t:ToSchema) instance for [servant-swagger](https://hackage.haskell.org/package/servant-swagger) support.
## Injecting multiple values
The library also has more general data type `WithFields a b` that injects fields of 'toJSON a' into `toJSON b`.
``` haskell
data NewsPatch = NewsPatch {
tags :: [Text]
, rating :: Double
}
$(deriveJSON defaultOptions ''NewsPatch)
```
``` haskell
let myNewsPatch = NewsPatch ["tag1", "tag2"] 42
in encode $ WithFields myNewsPatch myNews
```
``` json
{
"title": "Awesome piece of news!"
, "body": "Big chunk of text"
, "author": "Just Me"
, "timestamp": "2016-07-26T18:54:42.678999Z"
, "tags": ["tag1", "tag2"]
, "rating": 42.0
}
```
## Corner cases
Unfortunately, we cannot inject in non object values of produced JSON, so the library creates a wrapper object around non-object value:
``` haskell
encode (WithId 0 "non-object" :: WithId Int String)
```
``` json
{
"id": 0
, "value": "non-object"
}
```
The same story is about `WithFields` data type:
``` haskell
encode (WithFields 0 "non-object" :: WithFields Int String)
```
``` json
{
"injected": 0
, "value": "non-object"
}
```
For more examples and details, please, follow the haddocks.