deriving-aeson: Type driven generic aeson instance customisation

[ bsd3, generics, json, library ] [ Propose Tags ] [ Report a vulnerability ]

This package provides a newtype wrapper with FromJSON/ToJSON instances customisable via a phantom type parameter. The instances can be rendered to the original type using DerivingVia.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0, 0.1, 0.1.1, 0.1.2, 0.2, 0.2.1, 0.2.2, 0.2.3, 0.2.4, 0.2.5, 0.2.6, 0.2.6.1, 0.2.7, 0.2.8, 0.2.9, 0.2.10 (info)
Change log CHANGELOG.md
Dependencies aeson (>=1.4.7.0 && <2.3), base (>=4.12 && <5) [details]
Tested with ghc ==8.6.5, ghc ==8.8.3, ghc ==8.10.7, ghc ==9.2.5, ghc ==9.4.4
License BSD-3-Clause
Copyright Copyright (c) 2020 Fumiaki Kinoshita
Author Fumiaki Kinoshita
Maintainer fumiexcel@gmail.com
Category JSON, Generics
Bug tracker https://github.com/fumieval/deriving-aeson
Source repo head: git clone https://github.com/fumieval/deriving-aeson.git
Uploaded by FumiakiKinoshita at 2024-11-23T02:29:38Z
Distributions Arch:0.2.9, Fedora:0.2.9, LTSHaskell:0.2.10, NixOS:0.2.9, Stackage:0.2.10, openSUSE:0.2.9
Reverse Dependencies 16 direct, 53 indirect [details]
Downloads 19271 total (351 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2024-11-23 [all 1 reports]

Readme for deriving-aeson-0.2.10

[back to package description]

deriving-aeson

Hackage Haskell CI Discord

logo

This package provides a newtype wrapper where you can customise aeson's generic methods using a type-level interface, which synergises well with DerivingVia.

{-# LANGUAGE DerivingVia, DataKinds, DeriveGeneric #-}
import Data.Aeson
import Deriving.Aeson
import qualified Data.ByteString.Lazy.Char8 as BL

data User = User
  { userId :: Int
  , userName :: String
  , userAPIToken :: Maybe String
  } deriving Generic
  deriving (FromJSON, ToJSON)
  via CustomJSON '[OmitNothingFields, FieldLabelModifier '[StripPrefix "user", CamelToSnake]] User

testData :: [User]
testData = [User 42 "Alice" Nothing, User 43 "Bob" (Just "xyz")]

main = BL.putStrLn $ encode testData
-- [{"name":"Alice","id":42},{"api_token":"xyz","name":"Bob","id":43}]

Deriving.Aeson.Stock contains some aliases for even less boilerplates.

  • Prefixed str = CustomJSON '[FieldLabelModifier (StripPrefix str)]
  • PrefixedSnake str = CustomJSON '[FieldLabelModifier (StripPrefix str, CamelToSnake)]
  • Suffixed str = CustomJSON '[FieldLabelModifier (StripSuffix str)]
  • SuffixedSnake str = CustomJSON '[FieldLabelModifier (StripSuffix str, CamelToSnake)]
  • Snake = CustomJSON '[FieldLabelModifier '[StripPrefix str, CamelToSnake]]
  • Vanilla = CustomJSON '[]

How it works

The wrapper type has a phantom type parameter t, a type-level builder of an Option. Type-level primitives are reduced to one Option by the AesonOptions class.

newtype CustomJSON t a = CustomJSON { unCustomJSON :: a }

class AesonOptions xs where
  aesonOptions :: Options

instance AesonOptions xs => AesonOptions (OmitNothingFields ': xs) where
  aesonOptions = (aesonOptions @xs) { omitNothingFields = True }

...

You can use any (static) function for name modification by adding an instance of StringModifier.

data ToLower
instance StringModifier ToLower where
  getStringModifier "" = ""
  getStringModifier (c : xs) = toLower c : xs

Previous studies