2017-10-20 25 views
0

,我有以下的JSON数据导线/重写JSON价值

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,我对棱镜和遍历知识仍处于阶段发展,使我能够做到的唯一事情是:

#!/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} 
+0

您可能会发现通过调整问题可以更轻松一些。不要返回已经过滤的整个JSON blob,而是尝试返回两个部分:'data Result = Result {path :: [String],piece :: Value}'。然后你可以直接重建一个完整的JSON值。 –

回答

1

继具有用于路径的单独的数据类型的本杰明霍奇森的想法,下面是其使用透镜埃宋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是paramorphism即“破坏”一个Value起始形式叶子。在处理每个节点时,我们可以访问为其子节点获得的结果。

asum对于Maybe从左侧返回第一个Just

ifolded.asIndex产生一个映射关键字列表,或一个向量的整数索引列表。它们与当前节点的孩子的结果一一对应。