-- | -- Module: Data.Aeson.Prefix -- Maintainer: Jiri Marsicek -- -- Module provides 'flatten' which is used to flatten structure of 'Data.Aeson' JSON 'Value' as much as possible. -- -- == Examples -- -- === Basic objects -- -- @{ "a": { "b": 1 } }@ results in @{ "b": 1 }@ -- -- @{ "a": { "b": { "c": 1 } } }@ results in @{ "c": 1 }@ -- -- @{ "a": [ { "b": { "c": 1 } }, { "b": { "c": 2 } } ] }@ results in @{ "a": [ { "c": 1 }, { "c": 2 } ] }@ -- -- === Name conflicts result in data loss -- -- @{ "a": 1, "b": { "a": 2 } }@ results in @{ "a": 1 }@ -- -- * I don't know yet whether this is a feature or a bug :) module Data.Aeson.Flatten ( flatten -- * Utility functions , isObject , mergeTo ) where import Data.Aeson (Value(..)) import Data.Foldable (foldl') import qualified Data.HashMap.Strict as Map (elems, filter, union) -- | -- Predicate for testing whether provided 'Value' is a 'Object'. isObject :: Value -> Bool isObject (Object _) = True isObject _ = False -- | -- Merges keys from second 'Value' to first 'Value'. -- In case of name conflict, values from first are preserved. mergeTo :: Value -> Value -> Value mergeTo (Object o1) (Object o2) = Object $ Map.union o1 o2 mergeTo o _ = o -- | -- Recursively Flattens the structure of the provided JSON 'Value' flatten :: Value -> Value flatten (Array a) = Array $ flatten <$> a flatten (Object o) = let flat = flatten <$> o rest = Map.filter (not . isObject) flat in foldl' mergeTo (Object rest) $ Map.elems flat flatten v = v