Преобразовать поле значения JSON с помощью Aeson - PullRequest
0 голосов
/ 14 мая 2018

Сценарий: мне нужно прочитать в JSON-файл, а затем обновить поле значения в abcs до абсолютного пути.

Ключи, связанные с полями значений, не являются статичными, и поэтому я хотел бы выполнить это с помощью хеш-карт.

Моя задача в том, чтобы я продолжал ходить по типам и не мог понять, как их преобразовать. В идеале updatePaths должен возвращать объект ввода-вывода.

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: Чтобы лучше понять решение, я посмотрел на сигнатуру траверсного типа. Траверса имеет тип подписи: ... => (a -> f b) -> t a -> f (t b)

a должно быть Value;

f должно быть IO;

t должно быть HashMap Text, так как Object является синонимом типа для HashMap Text Value

Это дает нам: (Value -> IO Value) -> HashMap Text Value -> IO (HashMap Text Value) или IO Object по желанию.

1 Ответ

0 голосов
/ 15 мая 2018

Есть пара вопросов.Во-первых, у вас есть HashMap Aeson Value, а не String, поэтому нам нужно сопоставить с шаблоном, чтобы получить нашу "String".Во-вторых, «Строковый тип», который использует Aeson, равен Text, в то время как absolute_path хочет иметь обычную строку, поэтому нам нужно выполнить некоторое преобразование.В-третьих, absolute_path вернет значение IO, поэтому нам нужно будет использовать traverse вместо map.

Итак, если вы уже импортировали Data.Text как T

updatePaths :: Object -> IO Object
updatePaths obj = traverse createFullPath obj
  where 
    createFullPath (String val) = 
      (String . T.pack) <$> absolute_path ("resources/abcs/" ++ (T.unpack val))
    createFullPath x = pure x -- Ignore non strings
...