Home > Software engineering >  Aeson parse IHP payload to record type
Aeson parse IHP payload to record type

Time:12-02

What I am trying to do?

I am receing a JSON payload in IHP and would like to convert to record type.


What I have

Record type that I've generated with the IHP generators

data Resource' userId providerId bookings = Resource 
  { id :: (Id' "resources")
  , createdAt :: UTCTime
  , updatedAt :: UTCTime
  , userId :: userId
  , providerId :: providerId
  , name :: Text
  , bookings :: bookings
  , meta :: MetaBag
  } deriving (Eq, Show)
type Resource = Resource' (Id' "users") (Id' "providers")(QueryBuilder.QueryBuilder "bookings")

I wasn't able to directly convert the json payload to Record and therefore creating another temporary record to fetch values from json.

data ResourceCreateRequest = ResourceCreateRequest { name :: String }

instance FromJSON ResourceCreateRequest where
  parseJSON = withObject "Resource" $ \o -> ResourceCreateRequest <$> o .: "name"

JSON payload

{"name": "X"}

My idea was to use temp record to replace just few specific fields in IHP's generated newRecord @Resource. To show the results I am using (or trying to) in the controller

-- JRCR is a qualified name of the module with my temporary record
"application/json" -> renderJson (newRecord @Resource){ name = (JRCR.name getRequest) }

Question

Apparently I am having issues on Value -> Resource or Value -> ResourceCreateRequest conversion and cannot get it done correctly. Can you please help me out?

Current error

Record update is ambiguous, and requires a type signature
    * In the second argument of `($)', namely
        `(newRecord @Resource) {name = (JRCR.name getRequest)}'
      In the expression:
        renderJson $ (newRecord @Resource) {name = (JRCR.name getRequest)}
      In a case alternative:
          "application/json"
            -> renderJson
                 $ (newRecord @Resource) {name = (JRCR.name getRequest)}
   |
52 |           "application/json" -> renderJson $ (newRecord @Resource){ name = (JRCR.name getRequest) }
   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

CodePudding user response:

The problem is that type inference and DuplicateRecordFields don't work well together. Here's a minimal program that demonstrates the same problem you're having, but without involving IHP or any other third-party code:

{-# LANGUAGE DuplicateRecordFields, TypeApplications #-}

data Foo = Foo { name :: String } deriving Show
data Bar = Bar { name :: String } deriving Show

class Baz a where
    baz :: a

instance Baz Foo where
    baz = Foo ""

instance Baz Bar where
    baz = Bar ""

main :: IO ()
main = print $ (baz @Foo){ name = "qux" }

The solution here is to change baz @Foo to baz @Foo :: Foo. In this case, just baz :: Foo would work too.

Similarly, changing newRecord @Resource to newRecord @Resource :: Resource, or maybe newRecord :: Resource, should fix your code.

  • Related