Skip to main content

Ginger Support For Aeson 2

With ginger working with Aeson 2 in my local branch, I was able to start working on my projects that make use of aeson and ginger. The website software makes use of the Aeson Value type to represent metadata since most metadata is dynamic (defined by the user at runtime). In addition to being used in ginger template contexts, some metadata has to be processed, and I ended up writing a compatibility API for supporting Aeson 1 and Aeson 2. (I am thinking about releasing this code as a separate library.) Through this I realized that I overlooked a very obvious way to add support for Aeson 2 in ginger!

I had been thinking of the KeyMap type as a just a JSON object, but, while the key type is fixed, the value is parameterized. It is therefore possible to convert the values to GVal without changing the map data structure and use it within a GVal directly.

#if MIN_VERSION_aeson(2,0,0)
-- | 'AKM.KeyMap' of 'JSON.Value' becomes a dictionary-like 'GVal'
instance ToGVal m (AKM.KeyMap JSON.Value) where
    toGVal xs = helper (AKM.map toGVal xs)
        where
            helper :: AKM.KeyMap (GVal m) -> GVal m
            helper xs =
                def
                    { asHtml = mconcat . List.map (asHtml . snd) . AKM.toList $ xs
                    , asText = mconcat . List.map (asText . snd) . AKM.toList $ xs
                    , asBytes = mconcat . List.map (asBytes . snd) . AKM.toList $ xs
                    , asBoolean = not . AKM.null $ xs
                    , isNull = False
                    , asLookup = Just $ (`AKM.lookup` xs) . AK.fromText
                    , asDictItems = Just . List.map (\(k,v) -> (AK.toText k, v)) . AKM.toList $ xs
                    }
#endif

(Note: This code is written to match the style of the ginger source.)

I like this solution the best, since it makes use of whatever data structure Aeson is configured to use. I created a pull request with this implementation.