使用Aeson处理JSON:abcs字段值转绝对路径的类型问题
解决Haskell中JSON字段的绝对路径更新问题
针对你遇到的JSON中abcs字段值转绝对路径的类型问题,我来帮你梳理错误原因并给出解决方案:
问题回顾
你需要读取JSON文件,将动态键对应的abcs值更新为绝对路径,期望updatePaths返回IO Object类型,但一直被类型转换问题困扰。
示例JSON
{ "abcs": { "{crtl}": "crtl.abc", "{wt}": "wt.abc" } }
原代码及编译错误
你的原代码尝试用Hm.map处理Object,但存在两个核心类型问题:
absolute_path返回IO String,但你试图直接将它作为Value存入HashMap,导致IO String和Value类型不匹配Object是HashMap Text Value,而createFullPath期望参数是[Char],无法直接从Value转换
原代码:
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module TCT.ScenarioRunner where import Network.HTTP.Simple import Data.Aeson import GHC.Generics import qualified Data.ByteString.Lazy as B import qualified Data.HashMap.Strict as Hm import Data.Text.Internal import Filesystem.Path import System.Path.NameManip (absolute_path) loadFile :: IO B.ByteString loadFile = B.readFile "resources/initialise_body.json" initialise :: String -> IO () initialise sessionId = do raw <- loadFile let json = (eitherDecode raw) :: Either String Value case json of Left err -> putStrLn err Right (Object ps) -> case Hm.lookup "abcs" ps of Nothing -> putStrLn "Could not find abcs" Just (Object abcs) -> do (putStrLn "Found abcs") result <- print $ updatePaths abcs putStrLn "Bla" putStrLn "TBD: initialise" updatePaths :: Object -> Object updatePaths obj = Hm.map createFullPath obj where createFullPath val = absolute_path ("resources/abcs/" ++ val)
编译错误:
src/TCT/ScenarioRunner.hs:71:22-46: error: … • Couldn't match type ‘IO String’ with ‘Value’ Expected type: Object Actual type: Hm.HashMap Text (IO String) • In the expression: Hm.map createFullPath obj In an equation for ‘updatePaths’: updatePaths obj = Hm.map createFullPath obj where createFullPath val = absolute_path ("resources/abcs/" ++ val) | src/TCT/ScenarioRunner.hs:71:44-46: error: … • Couldn't match type ‘Value’ with ‘[Char]’ Expected type: Hm.HashMap Text [Char] Actual type: Object • In the second argument of ‘Hm.map’, namely ‘obj’ In the expression: Hm.map createFullPath obj In an equation for ‘updatePaths’: updatePaths obj = Hm.map createFullPath obj where createFullPath val = absolute_path ("resources/abcs/" ++ val) | Compilation failed.
正确解决方案
我们需要用traverse来处理带IO的HashMap映射,同时正确处理Value和Text/String的转换。修改后的updatePaths如下:
updatePaths :: Object -> IO Object updatePaths (obj :: Object) = traverse createFullPath obj where createFullPath :: Value -> IO Value createFullPath (String val) = (String . T.pack) <$> absolute_path ("resources/abcs/" ++ (T.unpack val)) createFullPath x = pure x -- 忽略非字符串类型的值
为什么这样可行?
我们来拆解traverse的类型签名:
… => (a -> f b) -> t a -> f (t b)
结合我们的场景:
a是Value:因为Object本质是HashMap Text Value,每个元素都是Valuef是IO:因为absolute_path是IO操作,我们需要将IO上下文保留到最终的IO Object中t是HashMap Text:对应Object的容器类型
代入后得到的具体类型就是:
(Value -> IO Value) -> HashMap Text Value -> IO (HashMap Text Value)
这完全匹配我们期望的IO Object类型,既解决了IO操作的上下文问题,也正确处理了Value和字符串类型的转换。
内容的提问来源于stack exchange,提问作者Casper Thule Hansen
相关产品推荐
相关产品推荐

