-- | Utilities to extend Aeson's parsing functionality. module Data.Aeson.ParseUtils where import Data.Aeson import Data.Yaml as YAML import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap import Data.ByteString (ByteString) import Data.Maybe (fromMaybe) -- | Like 'fromJSON', but takes a default object and merges it with the parsed -- JSON before decoding into a typed value. fromJSONWithDefaults :: (ToJSON a, FromJSON a) => a -> Value -> Result a fromJSONWithDefaults defaults = fromJSON . (mergeValues $ toJSON defaults) -- | Helper function to perform a deep merge on two JSON 'Value's. The merging -- semantics are defined as such: -- 1. If both values are objects, they are merged; collisions are resolved -- recursively. -- 2. If either value is @null@, the other takes precedence -- 3. Otherwise, the second value takes precedence mergeValues :: Value -> Value -> Value mergeValues (Object x) (Object y) = Object (HashMap.unionWith mergeValues x y) mergeValues x Null = x mergeValues _ x = x -- | Like 'YAML.decode', but takes a defaul object and merges it with the -- parsed JSON before decoding. decodeWithDefaults :: (ToJSON a, FromJSON a) => a -> ByteString -> Maybe a decodeWithDefaults defaults src = YAML.decode src >>= (resultToMaybe . fromJSONWithDefaults defaults) where resultToMaybe (Success x) = Just x resultToMaybe (Error e) = Nothing