遍历/重写JSON值

时间:2017-10-20 10:00:48

标签: haskell lens

我有以下json-data

value :: Maybe Value
value = decode
    "{ \"import\"  : { \"starttime\": \"2017-02-20T18:45:456.45645\" \
                   \ , \"endtime\"  : \"2017-02-20T18:45:456.45645\" \
                   \ } \
   \ , \"export\"  : { \"starttime\": \"2017-02-20T18:45:456.45645\" \
                   \ , \"endtime\"  : \"2017-02-20T18:45:456.45645\" \
                   \ } \
   \ , \"cleanup\" : { \"starttime\": \"2017-02-20T18:45:456.45645\" \
                   \ , \"endtime\"  : \"2017-02-20T18:45:456.45645\" \
                   \ , \"errormsg\" : \"It is dead Jim!\" \
                   \ } \
   \ }"

我的目标是重写这个对象,使它只包含"直接路径"给定的密​​钥 - 例如如果我搜索" errormsg"它应该只是

Just "{\"cleanup\":\"It is dead Jim!\"}"

Just "{\"cleanup\": {\"errormsg\":\"It is dead Jim!\"}}"

Nothing在密钥不存在的情况下,我对Prism和Traversals的了解仍然处于开发阶段,所以我唯一能做的就是:

#!/usr/bin/env stack
-- stack runhaskell --package=lens --package=aeson --package=lens-aeson-lens --package=bytestring
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Lens
import Data.Aeson
import Data.Foldable
import Data.Aeson.Lens
import Data.Maybe
import qualified Data.ByteString.Lazy.Char8 as B

value :: Maybe Value
value = decode
    "{ \"import\"  : { \"starttime\": \"2017-02-20T18:45:456.45645\" \
                   \ , \"endtime\"  : \"2017-02-20T18:45:456.45645\" \
                   \ } \
   \ , \"export\"  : { \"starttime\": \"2017-02-20T18:45:456.45645\" \
                   \ , \"endtime\"  : \"2017-02-20T18:45:456.45645\" \
                   \ } \
   \ , \"cleanup\" : { \"starttime\": \"2017-02-20T18:45:456.45645\" \
                   \ , \"endtime\"  : \"2017-02-20T18:45:456.45645\" \
                   \ , \"errormsg\" : \"It is dead Jim!\" \
                   \ } \
   \ }"

main :: IO ()
main = do
  traverse_ (traverse (B.putStrLn . encode))
            [ value & _Just . members %~ fromMaybe Null . preview (key "errormsg")
            , value & _Just . members %~ fromMaybe Null . preview (key "not here")
            ]

产生

{"export":null,"cleanup":"It is dead Jim!","import":null}
{"export":null,"cleanup":null,"import":null}

1 个答案:

答案 0 :(得分:1)

按照Benjamin Hodgson关于路径的单独数据类型的想法,这是一个使用 lens-aeson Control.Lens.Plated的可能解决方案:

import Control.Lens
import Control.Lens.Plated (para)
import Data.Foldable (asum)
import Data.Aeson
import qualified Data.Aeson.Lens
import Data.Text (Text)

data JsonPathPiece = Key Text | Index Int deriving Show

data JsonPath = JsonPath [JsonPathPiece] Value deriving Show

path :: Text -> Value -> Maybe JsonPath
path key = para go
    where
    go :: Value -> [Maybe JsonPath] -> Maybe JsonPath
    go v previous = case v of
        Object o  -> asum $ keyFound o : zipIntoMaybes Key o previous
        Array as  -> asum $ zipIntoMaybes Index as previous
        _         -> Nothing
    keyFound = preview (ix key.to (JsonPath [Key key]))
    zipIntoMaybes makePiece as mbs =
        zipWith fmap (toListOf (ifolded.asIndex.to makePiece.to addPiece) as) mbs
    addPiece piece (JsonPath pieces v) = JsonPath (piece:pieces) v

para是一种从群落中“摧毁”Value开始形式的共生。处理每个节点时,我们可以访问为其子节点获得的结果。

Maybe的{​​{3}}会从左侧返回第一个Just

asum生成地图的键列表,或矢量的整数索引列表。它们与当前节点的子节点的结果一一匹配。