Search code examples
jsonhaskellrecordhaskell-lens

How to automatically convert a record in "makeFields" lens format to JSON with fields matching lenses?


data ArticlePreview = ArticlePreview
  { _articlePreviewName          :: T.Text
  , _articlePreviewPerex         :: T.Text
  , _articlePreviewAuthorName    :: T.Text
  , _articlePreviewAuthorUrl     :: T.Text
  , _articlePreviewDate          :: T.Text
  , _articlePreviewCategoryName  :: T.Text
  , _articlePreviewCategoryUrl   :: T.Text
  , _articlePreviewCommentsCount :: Maybe Integer
  } deriving (Show, Eq, Generic)

makeFields ''ArticlePreview

I tried Aeson:

instance ToJSON ArticlePreview
instance FromJSON ArticlePreview

encodeToString :: ToJSON a => a -> String
encodeToString = CL.unpack . encode

Outputs:

{"_articlePreviewCommentsCount":17,"_articlePreviewAuthorName":"x","_articlePreviewName":"x","_articlePreviewCategoryName":"x","_articlePreviewAuthorUrl":"x","_articlePreviewCategoryUrl":"x","_articlePreviewDate":"x","_articlePreviewPerex":"x"}

Desired output:

{"commentsCount":17,"authorName":"x","name":"x","categoryName":"x","authorUrl":"x","categoryUrl":"x","date":"x","perex":"x"}

I don't insist on Aeson, but it must be automatic (without manual definition that e.g. _articlePreviewCommentsCount maps to commentsCount).


Solution

  • You can derive FromJSON/ToJSON using template Haskell from Data.Aeson.TH which allows for some modifications:

    import Data.Aeson.TH
    import Data.Char
    
    deriveJSON
      defaultOptions {fieldLabelModifier = (_head %~ toLower) . drop 15}
      ''ArticlePreview
    
    t :: ArticlePreview
    t = ArticlePreview "" "" "" "" "" "" "" (Just 12)
    

    Then you get:

    GHCI> encode t
    "{\"name\":\"\",\"perex\":\"\",\"authorName\":\"\",\"authorUrl\":\"\",\"date\":\"\",\"categoryName\":\"\",\"categoryUrl\":\"\",\"commentsCount\":12}"