使用 Monad 解析 JSON 文档以查找特定值?

Parsing a JSON document with a Monad to look for a specific value?

我是 Haskell 的初学者,我正在尝试使用 https://hackage.haskell.org/package/json-0.9.1/docs/Text-JSON.html 来解析 JSON 文档。

在我的任务中,我得到了一个 JSON 文档,我想 return 对应于 "index" 的值,例如在以下情况下:

{
    "root": [
        {
            "root1": 157538
        },
        {
            "root2": [
                {
                    "leaf21": 3
                }
            ]
        },
        {
            "root3": [
                {
                    "leaf31": "somestring"
                },
                {
                    "index": "foundit"
                }
            ]
        }
    ]
}

具体来说:如果有一个 JSON 文档和一个像 "root" -> "root3" -> "index" 这样的路径存在,我想 return"foundit",否则我想return没什么。文档中的其他所有内容都是任意的:root3、root2、root1、root 可能存在也可能不存在等。

现在我可以使用大量 case 语句和模式匹配来做到这一点,但在阅读 https://wiki.haskell.org/All_About_Monads 之后,我想知道是否有更好的方法使用类似于 Maybe Monad 和绵羊克隆示例的方法,但是我不确定如何编写绑定函数...

[在我的真实案例中,我寻求的价值实际上在文档中有 19 个深度,所以我有很多案例陈述]

请问您能否建议如何使用 Monad 来做到这一点?

是的,不需要那些 case 语句

您的猜测是正确的 - 但在这种情况下单子不是正确答案(没有双关语意1)。

这对 TraversalsPrismsLenses 来说是一项伟大的工作 - 当然还有伟大的 aeson-library and lens-aeson.

{-# LANGUAGE OverloadedStrings #-}
module Test where

import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import Data.Monoid ((<>))

jsonString = "{\"root\":[{\"root1\":157538}"
           <>          ",{\"root2\":[{\"leaf21\":3}]}"
           <>          ",{\"root3\":[{\"leaf31\":\"somestring\"}"
           <>                      ",{\"index\":\"foundit\"}]}]}"

val :: Maybe Value
val = decode jsonString

indexMaybe :: Maybe Value
indexMaybe = val ^? _Just . key "root"  . values
                          . key "root3" . values
                          . key "index"

那么这是做什么的?

  • decodeByteString 转换为 Maybe Value - Maybe 因为解析可能会失败!

  • 然后 (^?) 运算符预览遍历 - 即它通过 JSON 对象并遵循您提供的 json 路径;

为此你至少要知道通往 "index" 的路径,如果这条路径未知,你必须对 lenses/prisms/traversals 投入更多的研究或进行简单的树搜索在解析的对象上。

这里有一个剧透,供那些没有时间在 json 对象中搜索 "index" 的人使用:

search :: Text -> Value -> Maybe Value
search txt o@(Object o') = let f y@(Just x) _ = y
                               f _ v = search txt v
                            in case o ^? key txt of
                                 Nothing -> foldl' f Nothing o'
                                 x -> x
search txt a@(Array a') = let f y@(Just x) _ = y
                              f _ v = search txt v
                           in foldl' f Nothing a'
search _ _ = Nothing

备选方案

正如@MarkSeeman 已经提到的那样 - 简单的文本搜索可能效率更高。

1: 好吧,也许有点