提取 Aeson 对象内的嵌套 属性
Extract nested property inside Aeson object
如何使用 Data.Aeson
获得嵌套的 属性?
例如,当使用 Value
解码任意 JSON 字符串时,如下所示:
decode "{\"foo\":{\"bar0\":\"foobar0\",
\"bar1\":\"foobar1\"}}" :: Maybe Value
我最终得到这个:
Just (Object (fromList [("foo",Object (fromList [("bar1",String "foobar1"),("bar0",String "foobar0")]))]))
现在,我如何编写一个函数 [String] -> Object -> Maybe Value
来提取 Value
,如果有的话,按照提供的属性列表到达?
这个函数应该这样使用:
extractProperty ["foo", "bar0"] (Object (fromList [("foo",Object (fromList [("bar1",String "foobar1"),("bar0",String "foobar0")]))]))
==> Just (String "foobar0")
extractProperty ["foo", "bar0", "baz"] (Object (fromList [("foo",Object (fromList [("bar1",String "foobar1"),("bar0",String "foobar0")]))]))
==> Nothing
以下解决方案使用了 lens
和 lens-aeson
包:
{-# LANGUAGE FlexibleInstances #-}
import Control.Lens (view,pre,ix) -- from lens
import Control.Lens.Reified (ReifiedTraversal(..))
import Data.Aeson -- from aeson
import Data.Aeson.Lens (_Object) -- from lens-aeson
import Data.Text -- form text
instance Monoid (ReifiedTraversal s s s s) where
mempty = Traversal id
mappend (Traversal t1) (Traversal t2) = Traversal (t1 . t2)
extractProperty :: [Text] -> Object -> Maybe Value
extractProperty keys o =
view (pre telescope) (Object o)
where
telescope = runTraversal $ foldMap (\k -> Traversal $ _Object . ix k) keys
ReifiedTraversal
只是围绕 Traversal
的新类型,我们在其上定义了一个 Monoid
实例,以便轻松组合以相同类型开始和结束的遍历(类似于Endo
幺半群是如何工作的)。
在我们的例子中,遍历 _Object . ix k
从 Value
到 Value
。 ix
来自 Control.Lens.At
并在 Object
.
的属性图上建立索引
我们使用 pre
函数提取组合遍历的第一个结果(如果存在)。
编辑: 正如@cchalmers 在他的评论中提到的,没有必要声明一个孤儿实例,它只适用于 Endo
。另外 key k
与 _Object . ix k
.
相同
这是 extractProperty
的一个版本,它不使用 lens
,而是依赖于使用 foldr
组成 kleisli 箭头列表 Value -> Maybe Value
:
import qualified Data.HashMap.Strict as HM
extractProperty :: [T.Text] -> Object -> Maybe Value
extractProperty keys o = telescope keys (Object o)
where
telescope = foldr (>=>) return . map maybeKey
maybeKey k v = case v of
Object o -> HM.lookup k o
_ -> Nothing
也许 lens
在这种情况下有点矫枉过正。
另一种方法,使用单子绑定:
import Data.Text (Text)
import qualified Data.HashMap.Strict as HM
extractProperty :: [Text] -> Value -> Maybe Value
extractProperty [] v = Just v
extractProperty (k:ks) (Object o) = HM.lookup k o >>= prop ks
extractProperty _ _ = Nothing
如何使用 Data.Aeson
获得嵌套的 属性?
例如,当使用 Value
解码任意 JSON 字符串时,如下所示:
decode "{\"foo\":{\"bar0\":\"foobar0\",
\"bar1\":\"foobar1\"}}" :: Maybe Value
我最终得到这个:
Just (Object (fromList [("foo",Object (fromList [("bar1",String "foobar1"),("bar0",String "foobar0")]))]))
现在,我如何编写一个函数 [String] -> Object -> Maybe Value
来提取 Value
,如果有的话,按照提供的属性列表到达?
这个函数应该这样使用:
extractProperty ["foo", "bar0"] (Object (fromList [("foo",Object (fromList [("bar1",String "foobar1"),("bar0",String "foobar0")]))]))
==> Just (String "foobar0")
extractProperty ["foo", "bar0", "baz"] (Object (fromList [("foo",Object (fromList [("bar1",String "foobar1"),("bar0",String "foobar0")]))]))
==> Nothing
以下解决方案使用了 lens
和 lens-aeson
包:
{-# LANGUAGE FlexibleInstances #-}
import Control.Lens (view,pre,ix) -- from lens
import Control.Lens.Reified (ReifiedTraversal(..))
import Data.Aeson -- from aeson
import Data.Aeson.Lens (_Object) -- from lens-aeson
import Data.Text -- form text
instance Monoid (ReifiedTraversal s s s s) where
mempty = Traversal id
mappend (Traversal t1) (Traversal t2) = Traversal (t1 . t2)
extractProperty :: [Text] -> Object -> Maybe Value
extractProperty keys o =
view (pre telescope) (Object o)
where
telescope = runTraversal $ foldMap (\k -> Traversal $ _Object . ix k) keys
ReifiedTraversal
只是围绕 Traversal
的新类型,我们在其上定义了一个 Monoid
实例,以便轻松组合以相同类型开始和结束的遍历(类似于Endo
幺半群是如何工作的)。
在我们的例子中,遍历 _Object . ix k
从 Value
到 Value
。 ix
来自 Control.Lens.At
并在 Object
.
我们使用 pre
函数提取组合遍历的第一个结果(如果存在)。
编辑: 正如@cchalmers 在他的评论中提到的,没有必要声明一个孤儿实例,它只适用于 Endo
。另外 key k
与 _Object . ix k
.
这是 extractProperty
的一个版本,它不使用 lens
,而是依赖于使用 foldr
组成 kleisli 箭头列表 Value -> Maybe Value
:
import qualified Data.HashMap.Strict as HM
extractProperty :: [T.Text] -> Object -> Maybe Value
extractProperty keys o = telescope keys (Object o)
where
telescope = foldr (>=>) return . map maybeKey
maybeKey k v = case v of
Object o -> HM.lookup k o
_ -> Nothing
也许 lens
在这种情况下有点矫枉过正。
另一种方法,使用单子绑定:
import Data.Text (Text)
import qualified Data.HashMap.Strict as HM
extractProperty :: [Text] -> Value -> Maybe Value
extractProperty [] v = Just v
extractProperty (k:ks) (Object o) = HM.lookup k o >>= prop ks
extractProperty _ _ = Nothing