0

I have my model from models.persistentmodels

...

Thing
    title Text
    price Int 
    kosher Bool
    optionalstuff [Text] Maybe
    createdat UTCTime
    updatedat UTCTime
    deriving Show

...

It contains two time fields, which are UTCTime.

I am receiving via AJAX what is almost a Thing, in JSON. But the user JSON should not have createdat and updatedat or kosher. So we need to fill them in.

postNewEventR = do
    inputjson <- requireCheckJsonBody :: Handler Value
...
    -- get rawstringofthings from inputjson
...

    let objectsMissingSomeFields = case (decode (BL.fromStrict $ TE.encodeUtf8 rawstringofthings) :: Maybe [Object]) of
                        Nothing -> error "Failed to get a list of raw objects."
                        Just x  -> x

    now <- liftIO getCurrentTime

    -- Solution needs to go here:
    let objectsWithAllFields = objectsMissingSomeFields

    -- We hope to be done
    let things = case (eitherDecode $ encode objectsWithAllFields) :: Either String [Thing] of
                        Left err -> error $ "Failed to get things because: " <> err
                        Right xs  -> xs

The error "Failed to get things" comes here because the JSON objects we parsed are missing fields that are needed in the model.

1 Answers1

0

Solution

let objectsWithAllFields = Import.map (tackOnNeccessaryThingFields now True) objectsMissingSomeFields

So we take the current object and tack on the missing fields e.g. kosher and createdat.

But there is some strange difference in the way UTCTime is read vs aeson's way to parse UTCTime. So when I print UTCTime in to a Aeson String, I needed to print out the UTCTime into the format that it is expecting later:

tackOnNeccessaryThingFields :: UTCTime -> Bool -> Object -> Object
tackOnNeccessaryThingFields t b hm = G.fromList $ (G.toList hm) <> [
                                                                              ("createdat", String (pack $ formatTime defaultTimeLocale "%FT%T%QZ" t)),
                                                                              ("updatedat", String (pack $ formatTime defaultTimeLocale "%FT%T%QZ" t)),
                                                                              ("kosher", Bool b)
                                                                 ]

tackOnNeccessaryThingFields _ _ _ = error "This isn't an object."

After this fix, the object has all the fields needed to make the record, so the code gives [Thing]. And also the code runs without runtime error, instead of failing to parse the tshow t as UTCTime.

Note: This aeson github issue about this problem seems to be closed but it seems to be not any more permissive: https://github.com/bos/aeson/issues/197

Thanks to Artyom: https://artyom.me/aeson#records-and-json-generics

Thanks to Pbrisbin: https://pbrisbin.com/posts/writing_json_apis_with_yesod/

Thanks to Snoyman: For everything