Data.Aeson.Lens 的转换
Transformations with Data.Aeson.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 ()
JSON内容为:
{
"total_rows": 2,
"rows": [
{
"_id": { "$oid": "5548ed2671eab385baadf0a7" },
"abc": 123
},
{
"_id": { "$oid": "5548ed5171eab385baadf0a8" },
"def": 456
}
],
"query": {},
"offset": 0,
"millis": 0
}
输出为:
Just 2
Just "5548ed2671eab385baadf0a7"
我想达到这样的程度,输出是:
Just 2
Just [("abc",123),("def",456)]
换句话说,我想从 rows
键下的值中提取有用的内容,而无需事先知道该 JSON 数组的元素中的键是什么。
我还希望程序向用户发出信号,表明 query
键下的值是一个空的 JSON 对象。
首先使用lens-aeson
而不是aeson-lens
。 aeson-lens
好久没更新了,我觉得不是所有的镜头都中规中矩。 因为他们都共享 Data.Aeson.Lens
你得 运行 ghc-pkg unregister aeson-lens
(或 cabal sandbox hc-pkg unregister aeson-lens
,如果您使用的是沙箱)。
您没有准确描述您是如何知道 "useful contents" 是什么的,所以我假设它是任何未被称为 "_id"
的东西并指向一个整数。在 aeson-lens
中,数组的每个对象都是一个 HashMap
因此首先我们将遍历这些对象:
rows :: Traversal' Value (HM.HashMap Text Value)
rows = key "rows" . _Array . each . _Object
-- or key "rows" . values
对于行值,我们可以使用索引遍历,其中索引是 HashMap
的 Text
。然后我们可以使用 ifiltered
通过检查索引不是 "_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
从这里可以轻松获得您想要的列表:
> v ^@.. rows . nonIds
[("abc",123),("def",456)]
(^@..
) returns 索引遍历的列表,以及它的索引。
要检查 "query" 是否为空对象,您可以使用 nullOf
:
emptyQuery = nullOf (key "query" . _Object . each) v
这是一个工作示例(我将 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
给出输出
Just 2
[("abc",123),("def",456)]
True
(你可以在这里等效地使用 traverse
或 traversed
而不是 each
,它们都遍历 Vector
;我只是认为 each
读取更好。)
使用 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 ()
JSON内容为:
{
"total_rows": 2,
"rows": [
{
"_id": { "$oid": "5548ed2671eab385baadf0a7" },
"abc": 123
},
{
"_id": { "$oid": "5548ed5171eab385baadf0a8" },
"def": 456
}
],
"query": {},
"offset": 0,
"millis": 0
}
输出为:
Just 2
Just "5548ed2671eab385baadf0a7"
我想达到这样的程度,输出是:
Just 2
Just [("abc",123),("def",456)]
换句话说,我想从 rows
键下的值中提取有用的内容,而无需事先知道该 JSON 数组的元素中的键是什么。
我还希望程序向用户发出信号,表明 query
键下的值是一个空的 JSON 对象。
首先使用lens-aeson
而不是aeson-lens
。 aeson-lens
好久没更新了,我觉得不是所有的镜头都中规中矩。 因为他们都共享 Data.Aeson.Lens
你得 运行 ghc-pkg unregister aeson-lens
(或 cabal sandbox hc-pkg unregister aeson-lens
,如果您使用的是沙箱)。
您没有准确描述您是如何知道 "useful contents" 是什么的,所以我假设它是任何未被称为 "_id"
的东西并指向一个整数。在 aeson-lens
中,数组的每个对象都是一个 HashMap
因此首先我们将遍历这些对象:
rows :: Traversal' Value (HM.HashMap Text Value)
rows = key "rows" . _Array . each . _Object
-- or key "rows" . values
对于行值,我们可以使用索引遍历,其中索引是 HashMap
的 Text
。然后我们可以使用 ifiltered
通过检查索引不是 "_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
从这里可以轻松获得您想要的列表:
> v ^@.. rows . nonIds
[("abc",123),("def",456)]
(^@..
) returns 索引遍历的列表,以及它的索引。
要检查 "query" 是否为空对象,您可以使用 nullOf
:
emptyQuery = nullOf (key "query" . _Object . each) v
这是一个工作示例(我将 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
给出输出
Just 2
[("abc",123),("def",456)]
True
(你可以在这里等效地使用 traverse
或 traversed
而不是 each
,它们都遍历 Vector
;我只是认为 each
读取更好。)