How can I access nested JSON in IHP using param?

I’m working with an IHP controller that receives a JSON payload representing multiple records to process in a single request. Here’s a simplified example:

json

CopyEdit

{
  "changes": [
    {
      "type": "person",
      "method": "POST",
      "data": {
        "uuid": "f0ebe4b5-78af-5e27-b0cf-d103c5672a99",
        "label": "123",
        "health_center": "69f3ee80-6eab-4fe5-9e89-870692f14f64"
      }
    },
    {
      "type": "person",
      "method": "PATCH",
      "data": {
        "uuid": "d810c62c-1f7c-570f-94ef-834bdfb8f1f3",
        "label": "another new",
        "health_center": "69f3ee80-6eab-4fe5-9e89-870692f14f64"
      }
    }
  ]
}

Each item in the changes array contains a data object with the record’s fields.

I’d like to loop over the array and, for each data, treat it like form input so I can use IHP’s standard fill @'["... function.

Or maybe there’s a better way? One that doesn’t force me to write an FromJSON for each different records.

hmm, I think for now I’ll just go with FromJSON. It’s likely more “natural” that way.

-- Wrapper for the full payload.
data SyncUploadPayload = SyncUploadPayload
    { changes :: [SyncUploadItem]
    }

instance FromJSON SyncUploadPayload where
    parseJSON = withObject "SyncUploadPayload" \v ->
        SyncUploadPayload <$> v .: "changes"

-- Wrapper for a single record in the payload.
data SyncUploadItem
    = SyncUploadPerson PersonFromSync

instance FromJSON SyncUploadItem where
    parseJSON = withObject "SyncUploadItem" \v -> do
        t <- v .: "type"
        dataVal <- v .: "data"
        case (t :: Text) of
            "person" -> SyncUploadPerson <$> parseJSON dataVal
            _ -> fail ("Unsupported sync type: " <> cs t)

data PersonFromSync = PersonFromSync
    { uuid :: UUID
    , lastName :: Text
    , healthCenterId :: Id HealthCenter
    , createdAt :: UTCTime
    }
instance FromJSON PersonFromSync where
    parseJSON (Object v) =
        PersonFromSync
            <$> v
            .: "uuid"
            <*> v
            .:? "first_name"
            <*> v
            .:? "last_name"
            <*> v
            .: "health_center_id"
            <*> v
            .: "created_at"
    parseJSON _ = empty
1 Like

the FromJSON approach is what I was going to suggest :slight_smile: