如何检查解析的 Aeson 值?
How to inspect parsed Aeson Value?
如何浏览大 Aeson
Values
?我知道应该有一个我感兴趣的字符串嵌套在结构的某处。我怎样才能找到它?
到目前为止我只知道如何查询构造函数并发现它是一个数组。我怎样才能挖掘得更深?
> take 20 $ show bt
"Array (fromList [Obj"
lens
包具有检查树状结构的有用函数,如 JSON Value
s。还有 lens-aeson
包,带有额外的 JSON 特定功能。
import Data.Text
import Data.Aeson
import Data.Aeson.Lens (_Value,_String) -- this is from lens-aeson
import Data.Foldable (toList)
import Control.Lens (Fold,folding,universeOf,toListOf,paraOf,preview)
我们可以从定义一个镜头 Fold
开始,它提取给定 JSON Value
:
的直接子 Values
vchildren :: Fold Value Value
vchildren = folding $ \v -> case v of
Object o -> toList o
Array a -> toList a
_ -> []
folding
是 lens
的一个函数,它创建一个 Fold
一个 returns 列表的函数。 Value
的列表,在我们的例子中。
我们可以将 vchildren
与 universeOf
function from Control.Lens.Plated
结合起来得到一个函数,该函数提取 所有 Value
的传递后代,包括其自身:
allValues :: Value -> [Value]
allValues = universeOf vchildren
并且此函数提取 Value
中包含的所有文本。它使用 _String
prism from Data.Aeson.Lens
(a Prism
有点像可以传递的 "reified" 模式):
allTexts :: Value -> [Text]
allTexts = toListOf (folding allValues . _String)
Control.Lens.Plated
也有一些有趣的函数,比如 paraOf
,可以让你构建 "paramorphims"。 paramorphism 是一个 "controlled destruction" 的树状结构,从叶子开始,向上构建结果。例如这个函数
vpara :: (Value -> [r] -> r) -> Value -> r
vpara = paraOf vchildren
将另一个函数作为其第一个参数,该函数接收 "current node" 以及下方节点的中间结果,并为当前节点构建中间结果。
vpara
将开始使用叶子中的 JSON 值(这些节点的中间结果列表只是 []
)并向上处理。
vpara
的一种可能用途是获取 JSON 中以符合某些条件的文本结尾的路径列表,如下所示:
type Path = [Value]
pathsThatEndInText :: (Text -> Bool) -> Value -> [Path]
pathsThatEndInText pred = vpara func
where
func :: Value -> [[Path]] -> [Path]
func v@(String txt) _ | pred txt = [[v]]
func v l@(_:_) = Prelude.map (v:) (Prelude.concat l)
func _ _ = []
要获得对 pathsThatEndInText
返回的路径之一的可读性描述:
import qualified Data.HashMap.Strict as HM
import qualified Data.Vector as V
describePath :: Path -> [String]
describePath (v:vs) = Prelude.zipWith step (v:vs) vs
where
step (Object o) next = (unpack . Prelude.head . HM.keys . HM.filter (==next)) o
step (Array a) next = (show . maybe (error "not found") id) (V.elemIndex next a)
step _ _ = error "should not happen"
最后,这里有一个示例 JSON 用于在 ghci 中测试上述功能的值:
exampleJSON :: Value
exampleJSON = maybe Null id (preview _Value str)
where
str = "[{ \"k1\" : \"aaa\" },{ \"k2\" : \"ccc\" }, { \"k3\" : \"ddd\" }]"
这是 gist。
如何浏览大 Aeson
Values
?我知道应该有一个我感兴趣的字符串嵌套在结构的某处。我怎样才能找到它?
到目前为止我只知道如何查询构造函数并发现它是一个数组。我怎样才能挖掘得更深?
> take 20 $ show bt
"Array (fromList [Obj"
lens
包具有检查树状结构的有用函数,如 JSON Value
s。还有 lens-aeson
包,带有额外的 JSON 特定功能。
import Data.Text
import Data.Aeson
import Data.Aeson.Lens (_Value,_String) -- this is from lens-aeson
import Data.Foldable (toList)
import Control.Lens (Fold,folding,universeOf,toListOf,paraOf,preview)
我们可以从定义一个镜头 Fold
开始,它提取给定 JSON Value
:
Values
vchildren :: Fold Value Value
vchildren = folding $ \v -> case v of
Object o -> toList o
Array a -> toList a
_ -> []
folding
是 lens
的一个函数,它创建一个 Fold
一个 returns 列表的函数。 Value
的列表,在我们的例子中。
我们可以将 vchildren
与 universeOf
function from Control.Lens.Plated
结合起来得到一个函数,该函数提取 所有 Value
的传递后代,包括其自身:
allValues :: Value -> [Value]
allValues = universeOf vchildren
并且此函数提取 Value
中包含的所有文本。它使用 _String
prism from Data.Aeson.Lens
(a Prism
有点像可以传递的 "reified" 模式):
allTexts :: Value -> [Text]
allTexts = toListOf (folding allValues . _String)
Control.Lens.Plated
也有一些有趣的函数,比如 paraOf
,可以让你构建 "paramorphims"。 paramorphism 是一个 "controlled destruction" 的树状结构,从叶子开始,向上构建结果。例如这个函数
vpara :: (Value -> [r] -> r) -> Value -> r
vpara = paraOf vchildren
将另一个函数作为其第一个参数,该函数接收 "current node" 以及下方节点的中间结果,并为当前节点构建中间结果。
vpara
将开始使用叶子中的 JSON 值(这些节点的中间结果列表只是 []
)并向上处理。
vpara
的一种可能用途是获取 JSON 中以符合某些条件的文本结尾的路径列表,如下所示:
type Path = [Value]
pathsThatEndInText :: (Text -> Bool) -> Value -> [Path]
pathsThatEndInText pred = vpara func
where
func :: Value -> [[Path]] -> [Path]
func v@(String txt) _ | pred txt = [[v]]
func v l@(_:_) = Prelude.map (v:) (Prelude.concat l)
func _ _ = []
要获得对 pathsThatEndInText
返回的路径之一的可读性描述:
import qualified Data.HashMap.Strict as HM
import qualified Data.Vector as V
describePath :: Path -> [String]
describePath (v:vs) = Prelude.zipWith step (v:vs) vs
where
step (Object o) next = (unpack . Prelude.head . HM.keys . HM.filter (==next)) o
step (Array a) next = (show . maybe (error "not found") id) (V.elemIndex next a)
step _ _ = error "should not happen"
最后,这里有一个示例 JSON 用于在 ghci 中测试上述功能的值:
exampleJSON :: Value
exampleJSON = maybe Null id (preview _Value str)
where
str = "[{ \"k1\" : \"aaa\" },{ \"k2\" : \"ccc\" }, { \"k3\" : \"ddd\" }]"
这是 gist。