aeson-injector: Injecting fields into aeson values

[ data, json, library, mit, web ] [ Propose Tags ] [ Report a vulnerability ]

It is small utility library that is intented to be used in RESTful APIs, especially with servant and Swagger. Its main purpose is simple injection of fields into JSONs produced by aeson library.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 1.0.0.0, 1.0.0.1, 1.0.1.0, 1.0.2.0, 1.0.3.0, 1.0.4.0, 1.0.5.0, 1.0.5.1, 1.0.6.0, 1.0.7.0, 1.0.8.0, 1.0.9.0, 1.0.10.0, 1.1.0.0, 1.1.1.0, 1.1.2.0, 1.1.3.0, 1.1.4.0, 1.1.5.0, 1.1.6.0, 1.2.0.0, 2.0.0.0
Change log CHANGELOG.md
Dependencies aeson (>=2.1 && <2.2), attoparsec (>=0.14 && <0.15), base (>=4.7 && <4.17), bifunctors (>=5.2 && <6), deepseq (>=1.4 && <2), hashable (>=1.0 && <2.0), lens (>=4.13 && <5.2), servant-docs (>=0.7 && <0.13), swagger2 (>=2.4 && <3.0), text (>=1.2 && <2.1), unordered-containers (>=0.2.7 && <0.3) [details]
Tested with ghc ==8.8.1, ghc ==8.10.7, ghc ==9.0.2
License MIT
Copyright 2016-2017 Anton Gushcha
Author Anton Gushcha
Maintainer ncrashed@gmail.com
Category Data, JSON, Web
Source repo head: git clone https://github.com/NCrashed/aeson-injector
Uploaded by NCrashed at 2023-11-21T14:23:18Z
Distributions
Reverse Dependencies 7 direct, 1 indirect [details]
Downloads 12691 total (34 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for aeson-injector-2.0.0.0

[back to package description]

aeson-injector

Build Status

It is small utility library that is intented to be used in RESTful APIs, especially with servant and Swagger. Its main purpose is simple injection of fields into JSONs produced by aeson library.

Consider the following common data type in web service developing:

data News = News {
  title :: Text
, body :: Text
, author :: Text
, timestamp :: UTCTime  
}

-- Consider we have simple 'ToJSON' and 'FromJSON' instances
$(deriveJSON defaultOptions ''News) 

ToJSON instance produces JSON's like:

{
  "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:

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:

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:

>>> encode (WithField 42 myNews :: WithField "id" NewsId News)
{
  "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 instance for 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.

data NewsPatch = NewsPatch {
  tags :: [Text]
, rating :: Double
}
$(deriveJSON defaultOptions ''NewsPatch) 
let myNewsPatch = NewsPatch ["tag1", "tag2"] 42 
in encode $ WithFields myNewsPatch myNews
{
  "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:

encode (WithId 0 "non-object" :: WithId Int String)
{
  "id": 0 
, "value": "non-object"
}

The same story is about WithFields data type:

encode (WithFields 0 "non-object" :: WithFields Int String)
{
  "injected": 0 
, "value": "non-object"
}

For more examples and details, please, follow the haddocks.