使用 Data.Aeson.Lens 进行转换

Fal*_*ies 1 haskell aeson haskell-lens

使用aeson-lens,我编写了这个程序,它使我非常接近我想要实现的目标:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Conduit ( simpleHttp )
import Data.Aeson           ( decode
                            , Value
                            )
import Data.Maybe           ( fromJust )
import Control.Lens         ( (^.) )
import Data.Aeson.Lens      ( key, nth )

main :: IO ()
main = do
  pageContents <- simpleHttp "http://127.0.0.1:28017/baseball/team/"
  let v = decode pageContents :: Maybe Value
  let totalRowsVal = v ^. key "total_rows" :: Maybe Int
      oidVal       = v ^. key "rows" . nth 0 ^. key "_id" ^. key "$oid" :: Maybe String
  print totalRowsVal
  print oidVal
  return ()
Run Code Online (Sandbox Code Playgroud)

JSON 内容为:

{
  "total_rows": 2,
  "rows": [
    {
      "_id": { "$oid": "5548ed2671eab385baadf0a7" },
      "abc": 123
    },
    {
      "_id": { "$oid": "5548ed5171eab385baadf0a8" },
      "def": 456
    }
  ],
  "query": {},
  "offset": 0,
  "millis": 0
}
Run Code Online (Sandbox Code Playgroud)

输出是:

Just 2
Just "5548ed2671eab385baadf0a7"
Run Code Online (Sandbox Code Playgroud)

我想知道输出是:

Just 2
Just [("abc",123),("def",456)]
Run Code Online (Sandbox Code Playgroud)

换句话说,我想从rows键下的值中提取有用的内容,而无需事先知道该 JSON 数组的元素中的键是什么。我还希望程序向用户发出信号,该query键下的值是一个空的 JSON 对象。

cch*_*ers 5

首先使用lens-aesonnot aeson-lensaeson-lens有一段时间没有更新了,我不认为它的所有镜头都是守法的。由于它们都共享,因此Data.Aeson.Lens您必须运行ghc-pkg unregister aeson-lens(或者cabal sandbox hc-pkg unregister aeson-lens如果您使用的是沙箱)。

你还没有确切地描述你如何知道“有用的内容”是什么,所以我假设它没有被调用"_id"并指向一个整数。在aeson-lens数组的每个对象中,HashMap首先我们将遍历这些对象:

rows :: Traversal' Value (HM.HashMap Text Value)
rows = key "rows" . _Array . each . _Object
-- or  key "rows" . values
Run Code Online (Sandbox Code Playgroud)

对于行值,我们可以使用索引遍历,其中指数是TextHashMap。然后我们可以ifiltered通过检查索引 is not来过滤内容"_id"。我们也习惯于_Integer只获取指向Integer.

nonIds :: IndexedTraversal' Text (HM.HashMap Text Value)  Integer
nonIds = itraversed . ifiltered (\i _ -> i /= "_id") . _Integer
-- or    members . ifiltered (\i _ -> i /= "_id") . _Integer
Run Code Online (Sandbox Code Playgroud)

从这里很容易得到你想要的清单:

> v ^@.. rows . nonIds
[("abc",123),("def",456)]
Run Code Online (Sandbox Code Playgroud)

( ^@..) 返回索引遍历的列表及其索引。

要检查“查询”是一个空对象,您可以使用nullOf

emptyQuery = nullOf (key "query" . _Object . each) v
Run Code Online (Sandbox Code Playgroud)

这是一个工作示例(我将 json 文件保存到“json.json”:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Data.Aeson.Lens
import qualified Data.ByteString.Lazy as LB
import Control.Lens

main :: IO ()
main = do
  contents <- LB.readFile "json.json"
  let Just v = decode contents :: Maybe Value
  let totalRows  = v ^? key "total_rows" . _Integer
      rows       = key "rows" . values
      nonIds     = members . ifiltered (\i _ -> i /= "_id") . _Integer
      vals       = v ^@.. rows . nonIds
      emptyQuery = nullOf (key "query" . members) v
  print totalRows
  print vals
  print emptyQuery
Run Code Online (Sandbox Code Playgroud)

这给出了输出

Just 2
[("abc",123),("def",456)]
True
Run Code Online (Sandbox Code Playgroud)

(你可以等效地使用traverseortraversed代替each这里,它们都遍历Vector; 我只是认为each读起来更好。)