将任意嵌套的JSON对象解析/导出到Haskell中的映射

时间:2014-06-11 23:05:24

标签: json haskell aeson

我需要能够将一个巨大的嵌套JSON结构解析成更具延展性的东西,最好是从字符串到字符串的映射。

我正在谈论的各种结构的例子:

  

{“foo”:“baz”,“bar”:{“qux”:“quux”,“baz”:{“abracadabra”:“alakazam”,“abc”:“xyz”}}}

我想将上述内容解析为:

  

fromList [(“foo”,“baz”),(“bar / qux”,“quux”),(“bar / baz / abracadabra”,“alakazam”),(“bar / baz / abc”, “XYZ”)]

是否可以使用Aeson的相对库存解析器?我是否应该对Aeson解析的JSON AST进行一些修改,直到它起作用?

2 个答案:

答案 0 :(得分:4)

我在这里无法自拔,我 滥用lens ......

在下面的示例中,Primitive只是叶子JSON值的数据类型。

import Data.Aeson
import Control.Lens
import Data.Aeson.Lens

flatten :: Value -> [(String, Primitive)]
flatten (Object obj) = do
    (k, v) <- obj^@..ifolded
    flatten v <&> _1 <>~ (k^..each ++ "/")
flatten (Array  arr) = arr^..each >>= flatten
flatten prim         = [("", prim^?! _Primitive)]

input = "{ \"foo\" : \"baz\", \"bar\" : {\"qux\" : \"quux\", \"baz\" : {\"abracadabra\" : \"alakazam\", \"abc\" : \"xyz\" } } }"

main = do
    print $ input^? _Value . to flatten
    -- Just [("foo/",StringPrim "baz"),("abc/baz/bar/",StringPrim "xyz"),("abracadabra/baz/bar/",StringPrim "alakazam"),("qux/bar/",StringPrim "quux")]

答案 1 :(得分:2)

module AesonFun(collapse) where

import qualified Data.Map as M (empty, Map, insert, lookup)
import qualified Data.HashMap.Strict as HM (toList)
import qualified Data.List as L (foldl')
import qualified Data.Aeson as Ae (decode, )
import qualified Data.Text as T (unpack, Text)
import Data.Aeson.Types as AT
import qualified Data.ByteString.Lazy.Char8 as BS ( pack )

collapse :: String -> StringMap
collapse s = maybe M.empty (collapser0 M.empty) $ toValue s

toValue :: String -> Maybe Value
toValue = Ae.decode . BS.pack

type StringMap = M.Map String String

delim = "/"

type Collapser = StringMap -> Value -> StringMap

collapser0 = collapser Nothing

collapser :: (Maybe String) -> Collapser
collapser s m v = case v of
                    AT.Object ob  -> L.foldl' (\m' (c,v') -> c m' v')  m pairs where
                      pairs :: [(Collapser, Value)]
                      pairs = map toPair $ HM.toList ob
                      toPair :: (T.Text, Value) -> (Collapser, Value)
                      toPair (t, v') = (collapser s', v') where
                        s' = case s of
                               Just p  -> Just $ p ++ delim ++ (T.unpack t)
                               Nothing -> Just $ (T.unpack t)
                    AT.String t  -> maybe m (\str -> M.insert str (T.unpack t) m) s
                    otherwise    -> m

AesonFun> collapse "{ \"foo\" : \"baz\", \"bar\" : {\"qux\" : \"quux\", \"baz\" : {\"abracadabra\" : \"alakazam\", \"abc\" : \"xyz\" } } }"
fromList [("bar/baz/abc","xyz"),("bar/baz/abracadabra","alakazam"),("bar/qux","quux"),("foo","baz")]