iam*_*nat 6 json haskell aeson haskell-lens
我对镜头的魔力很新,所以我遇到了一些麻烦.
参考:https://www.fpcomplete.com/user/tel/lens-aeson-traversals-prisms
可以通过以下方式遍历JSON对象:
val ^? nth 0 . key "someObject" . key "version" . nth 2
Run Code Online (Sandbox Code Playgroud)
对于类似于的JSON对象:
"[{\"someObject\": {\"version\": [1, 0, 3]}}]"
Run Code Online (Sandbox Code Playgroud)
Maybe Monad在整个过程中使用,所以如果任何"访问者"失败,我会得到一个Nothing.
我也想传播失败,以便我知道访问者失败了.
我能想到的唯一方法就是传递一组访问器,按顺序应用它们,并在任何失败点返回错误.像这样的东西:
import Data.Aeson
import Data.Text
import Data.Vector ((!?))
import qualified Data.HashMap.Strict as HM
data MyAccessor = Nth Int | Key Text
withFailure :: Value -> [MyAccessor] -> Either String Value
withFailure val [] = Right val
withFailure val (x:xs) = case x of
Nth i -> case val of
(Array val') -> case (val' !? i) of
Just e -> withFailure e xs
_ -> Left $ "Could not get index " ++ (show i)
_ -> Left $ "Expected JSON array for index " ++ (show i)
Key k -> case val of
(Object val') -> case (HM.lookup k val') of
Just e -> withFailure e xs
_ -> Left $ "Could not get key " ++ (unpack k)
_ -> Left $ "Expected JSON object for key " ++ (unpack k)
Run Code Online (Sandbox Code Playgroud)
所以这个:
-- val = [[1,0,3], {"name" : "value"}]
> withFailure val [Nth 1, Key "name", Key "hello"]
Left "Expected JSON object for key hello"
> withFailure val [Nth 1, Key "name"]
Right (String "value")
Run Code Online (Sandbox Code Playgroud)
有更优雅的方式吗?制作一个Either-ish monad供镜头使用,结果就像是什么withFailure?
另一种可能性是使用来自 的单子折叠Control.Lens.Action。
Monadic 折叠可让您在折叠中加入有效的操作,以便这些操作与“探索”数据结构的过程交织在一起。
请注意,这与类似的东西不同mapMOf。Monadic 折叠允许您执行一些操作,例如创建折叠“动态”探索的结构部分,例如从磁盘加载它们,或询问用户输入。
普通折叠可直接用作单一折叠。您只需使用专门的运算符(例如(^!!)和 )来运行它们(^!?)。
要将效果引入单子折叠,请使用该act函数。
我们可以创建一个在 monad 中工作的 monadic 折叠Writer,并在“记录”进度的折叠中插入操作。像这样的东西:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad
import Control.Monad.Writer
import Control.Lens
import Data.Monoid
import Data.Aeson
import Data.Aeson.Lens
msg :: String -> IndexPreservingAction (Writer (Last String)) a a
msg str = act $ \a -> tell (Last . Just $ str) >> return a
main :: IO ()
main = do
let str = "[{\"someObject\": {\"version\": [1, 0, 3]}}]"
val = maybe (error "decode err") id . decode $ str :: Value
monfol = nth 0 . msg "#1"
. key "someObject" . msg "#2"
. key "version" . msg "#3"
. nth 2
(mresult,message) = runWriter $ val ^!? monfol
putStrLn $ case mresult of
Just result -> show result
Nothing -> maybe "no messages" id . getLast $ message
Run Code Online (Sandbox Code Playgroud)
如果更改 JSON 中的“version”键导致折叠失败,错误消息将为“#2”。
最好使用某种错误单子(例如,Either而不是 )Writer,以便能够准确定位失败的位置,而不是最后一个“检查点”。但我不确定这是否可能,因为折叠已经通过返回表示失败Nothing。
Control.Lens.Reified模块的新类型ReifiedMonadicFold为单子折叠提供了一些有用的实例。ReifiedMonadicFolds 的行为有点像单子的克莱斯利箭头,单子是 的一个实例MonadPlus。
| 归档时间: |
|
| 查看次数: |
298 次 |
| 最近记录: |