2012-11-06 2 views
4

Mon but est d'écrire un programme dans Haskell qui prend le nom d'un fichier json et interprète le reste des arguments comme un chemin pour naviguer dans ce fichier json et imprimer la valeur naviguée à. Le problème est que JSON peut contenir plusieurs types de valeurs, je ne sais pas comment faire pour que le système de type Haskell comprenne ce que je veux. Voici le code Haskell avec la fonction « Navigate » Je ne suis pas en mesure de mettre en œuvre correctement:Navigation dans les objets JSON d'une manière générique dans Haskell

import qualified Data.Aeson as A 
import qualified Data.ByteString.Char8 as BS 
import qualified Data.ByteString.Lazy.Char8 as BSL 
import Data.List 
import Data.Maybe 
import System.Environment 

parse :: String -> A.Value 
parse = fromJust . A.decode . BSL.pack 

isInteger xs = case reads xs :: [(Integer, String)] of 
    [(_, "")] -> True 
    _ -> False 

navigate :: A.Value -> String -> String 
navigate value [] = value 
navigate value [x:xs] 
    | isInteger x = ??? -- value is an array, get the xth element of it. 
    | otherwise = ??? -- value is an map, x is a key in it. 

main :: IO() 
main = do 
    [filename:path] <- getArgs 
    contents <- readFile filename 
    let d = parse contents 
    putStrLn (show (navigate d path)) 

Pour référence, voici comment le même programme aurait été écrit en Python:

from json import load 
from sys import argv  
def navigate(obj, path): 
    if not path: 
     return obj 
    head, tail = path[0], path[1:] 
    return navigate(obj[int(head) if head.isdigit() else head], tail)  
if __name__ == '__main__': 
    fname, path = argv[1], argv[2:] 
    obj = load(open(fname)) 
    print navigate(obj, path) 

le programme serait géré comme ceci:

$ cat data.json 
{"foo" : [[1, 2, 3, {"bar" : "barf"}]]} 
$ python showjson.py data.json foo 0 3 bar 
barf 
+0

Analysez chaque argument avec l'analyse JSON et écrivez une fonction qui indexe en une valeur JSON avec une autre valeur JSON, et combinez-la comme vous l'avez fait en Python. – augustss

+0

Notez que lorsque le motif correspond à des cellules ('x: xs'), vous devez utiliser' (x: xs) '(les parenthèses sont juste pour le regroupement, plutôt qu'une syntaxe spéciale). '[x: xs]' correspond à une liste contenant une liste unique, avec 'x' la tête et' xs' la queue de la liste interne, par ex. '[[1,2,3]]' => 'x = 1, xs = [2,3]'. – huon

+0

Merci pour l'info! –

Répondre

3

vous pouvez tout simplement match de motif sur les constructeurs de A.Value afin de déterminer quel type d'objet JSON que vous avez affaire w ième:

import qualified Data.HashMap.Strict as M 
import qualified Data.Vector as V 
import qualified Data.Text as T 

-- ... rest of the code more or less as before ... 

navigate :: A.Value -> [String] -> BSL.ByteString 
navigate value  []  = A.encode value 
navigate (A.Array vs) (x : xs) = navigate (vs V.! read x) xs 
navigate (A.Object o) (x : xs) = navigate (o M.! T.pack x) xs 

Notez que la définition de A.Value se présente comme suit:

data Value 
    = Object !(HashMap Text Value) 
    | Array !(Vector Value) 
    | ... -- other constructors 

Le code navigate fait ainsi l'utilisation de la fonction de recherche (appelé ! dans les deux cas) sur des vecteurs et des cartes de hachage . La fonction read est utilisée pour interpréter un argument de ligne de commande en tant que nombre si nécessaire (et échouera horriblement si ce n'est pas le cas), alors que T.pack réinterprète la chaîne comme une valeur de type Text.

Questions connexes