使用aeson转换JSON值字段

时间:2018-05-14 20:56:59

标签: haskell hashmap aeson

场景:我需要读取JSON文件,然后将abcs中的值字段更新为绝对路径。

与值字段相关的键不是静态的,因此我想使用哈希映射执行它。

我的挑战是,我会继续在各种类型中进行循环,并且无法弄清楚如何转换它们。理想情况下,updatePaths应返回IO Object。

JSON:

{
    "abcs": {
        "{crtl}": "crtl.abc",
        "{wt}": "wt.abc"
    }
}

Haskell:

{-# 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.

更新 问题解决方案几乎是正确的,我做了一些改动,因此解决方案变为:

 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 -- Ignore non strings

更新2: 为了更好地理解解决方案,我查看了遍历类型签名。 Traverse具有类型签名:... => (a -> f b) -> t a -> f (t b)

a必须是Value;

f必须是IO;

t必须为HashMap Text,因为ObjectHashMap Text Value的类型同义词

这为我们提供了(Value -> IO Value) -> HashMap Text Value -> IO (HashMap Text Value)或IO对象。

1 个答案:

答案 0 :(得分:1)

有几个问题。首先,你有一个Aeson的HashMap public static class IEnumerableExt { public static T FirstOrDefault<T>(this IEnumerable<T> src, Func<T, bool> testFn, T defval) => src.Where(aT => testFn(aT)).DefaultIfEmpty(defval).First(); } public static class StringExt { public static int IndexOf(this string source, string match, StringComparer sc) { return Enumerable.Range(0, source.Length) // for each position in the string .FirstOrDefault(i => // find the first position where either // match is Equals at this position for length of match (or to end of string) or sc.Equals(source.Substring(i, Math.Min(match.Length, source.Length-i)), match) || // match is Equals to on of the substrings beginning at this position Enumerable.Range(1, source.Length-i-1).Any(ml => sc.Equals(source.Substring(i, ml), match)), -1 // else return -1 if no position matches ); } } ,而不是Value所以我们需要模式匹配才能得到我们的&#34; String&#34;。其次是&#34; String类型&#34; Aeson使用的是String,而Text需要常规字符串,因此我们需要进行一些转换。第三,absolute_path将返回absolute_path值,因此我们需要使用IO而不是traverse

因此,假设您已将map导入Data.Text

T