2017-10-20 31 views
0

je les données JSON suivantesTraverse/Réécrire une valeur 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!\" \ 
        \ } \ 
    \ }" 

et mon objectif serait de réécrire cet objet tel qu'il ne contient que le « chemin direct » à une clé donnée - par exemple si je recherche « errormsg » dans le cas ne devrait être

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

ou

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

et Nothing où la clé est pas présent, mes connaissances sur Prismes et Traversées est encore au stade de développement de sorte que la seule chose que je réussi à faire est:

#!/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") 
      ] 

qui donne

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

Vous pourriez trouver cela un peu plus facile avec un ajustement au problème. Plutôt que de renvoyer un blob JSON entier qui a été filtré, essayez de renvoyer les deux morceaux: 'data Résultat = Résultat {chemin :: [Chaîne], pièce :: Valeur}'. Vous pouvez ensuite directement reconstruire une valeur JSON entière à partir de cela. –

Répondre

1

Après l'idée de Benjamin Hodgson d'avoir un type de données séparées pour les chemins, voici une solution qui utilise lentille Aeson et 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 est un paramorphisme que « détruit » une Value forme à partir les feuilles. Lors du traitement de chaque noeud, nous avons accès aux résultats obtenus pour ses enfants.

asum pour Maybe renvoie le premier Just à partir de la gauche.

ifolded.asIndex produit la liste des clés d'une carte ou la liste des indices d'entier pour un vecteur. Ils sont appariés un pour un avec les résultats pour les enfants du nœud actuel.