我的目标是用haskell编写一个程序,该程序采用json文件的名称,并将其余参数解释为导航json文件的路径,并打印导航到的值。问题在于JSON可以包含多个值类型,我不知道如何让Haskell的类型系统理解我想要的下面是haskell代码,其中包含我无法正确实现的“navigate”函数:
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))
作为参考,下面是用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)
程序将如下运行:
$ cat data.json
{"foo" : [[1, 2, 3, {"bar" : "barf"}]]}
$ python showjson.py data.json foo 0 3 bar
barf
最佳答案
您可以简单地对A.Value
的构造函数进行模式匹配,以确定您正在处理的是哪种JSON对象:
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
注意
A.Value
的定义如下:data Value
= Object !(HashMap Text Value)
| Array !(Vector Value)
| ... -- other constructors
因此,
navigate
的代码在向量和散列映射上使用查找函数(在两种情况下都称为!
)。函数read
用于在需要时将命令行参数解释为一个数字(如果不是,它将失败得可怕),而T.pack
则将字符串重新解释为Text
类型的值。