dciug
dciug

Reputation: 23

Aeson Encoding nested records

Suppose there's this record:

data Place = Place
  { details :: PlaceDetails
  , amenities :: [Amenity]
  , photos :: [PlacePhoto]
  } deriving (Generic)

I've implemented toEncoding for every data type, but I'd like to combine those fields in the Place type. For now, it's like this:

instance ToJSON Place where
  toEncoding Place{..} = 
    pairs  $ "details" .= details
          <> "amenities" .= amenities
          <> "photos" .= photos

I use toEncoding to preserve a certain order that I want. However, I'd like to remove that details key from the encoding, so that all the fields from PlaceDetails are on the same level as amenities and photos, without manually specifying all those fields, as PlaceDetails is quite big. How would I do that? I know that I can merge [Value] using HML.unions but I've only managed to do that with toJSON, not toEncoding.

What I currently have:

{
    "details": {
        "id": "place_6de6cda0f8524a6f9c264c84afdbadad",
        "name":"somename",
        "description":"some description", 
        "other": "a lot more fields"
    }
    "amenities": []
    "photos": []
}

This is what I want:

{
    "id":"place_6de6cda0f8524a6f9c264c84afdbadad",
    "name":"somename",
    "description":"some description", 
    "other": "a lot more fields",
    "amenities": []
    "photos": []
}

Thanks.

Upvotes: 1

Views: 198

Answers (1)

Thomas M. DuBuisson
Thomas M. DuBuisson

Reputation: 64740

An Aeson Object is just a hashmap which you can union using <> (infix) or mappend.

For example:

#!/usr/bin/env cabal
{- cabal:
    build-depends: base, aeson
-}
{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE RecordWildCards   #-}

module Main where

import Data.Aeson
import GHC.Generics

main :: IO ()
main = print (encode (Place (Foo 1 2) (Bar 3 4)))

data Foo = Foo {foo1 :: Int, foo2 :: Int}
    deriving (Eq,Ord,Show,Generic,ToJSON)

data Bar = Bar {bar1 :: Int, bar2 :: Int}
    deriving (Eq,Ord,Show,Generic,ToJSON)

data Place = Place { foo :: Foo , bar :: Bar }
    deriving (Eq,Ord,Show,Generic)

instance ToJSON Place where
  toJSON Place{..} =
       let Object b = toJSON bar
       in Object ([("foo",toJSON foo)] <> b)

And we can run this:

% chmod +x so.hs && ./so.hs
... snippet out build info ...
"{\"bar1\":3,\"foo\":{\"foo2\":2,\"foo1\":1},\"bar2\":4}"

Upvotes: 1

Related Questions