В моем текущем проекте "Learning Haskell" я пытаюсь получить данные о погоде из стороннего API.Я хочу извлечь значения name
и main.temp
из следующего тела ответа:
{
...
"main": {
"temp": 280.32,
...
},
...
"name": "London",
...
}
Я написал сервис getWeather
для выполнения ввода-вывода и преобразования ответа для построения GetCityWeather
данных:
....
data WeatherService = GetCityWeather String Double
deriving (Show)
....
getWeather :: IO (ServiceResult WeatherService)
getWeather = do
...
response <- httpLbs request manager
...
-- work thru the response
return $ case ((maybeCityName response, maybeTemp response)) of
(Just name, Just temp) -> success name temp
bork -> err ("borked data >:( " ++ show bork))
where
showStatus r = show $ statusCode $ responseStatus r
maybeCityName r = (responseBody r)^?key "name"._String
maybeTemp r = (responseBody r)^?key "main".key "temp"._Double
success n t = Right (GetCityWeather (T.unpack n) t)
err e = Left (SimpleServiceError e)
Я застрял, оптимизируя часть анализа JSON в maybeCityName
и maybeTemp
, мои мысли:
- В настоящее время JSON анализируется дважды (я применяю
^?
два раза по необработанному ответу responseBody r
). - Я хотел бы получить данные «одним выстрелом».
?..
может получить список значений.Но я извлекаю различные типы (String
, Double
), поэтому ?..
здесь не подходит.
Я ищу более элегантные / более естественные способы безопасного анализа JSON, читайтежелаемые значения и применить их к конструктору данных GetCityWeather
.Заранее благодарен за любую помощь и обратную связь.
Обновление: используя складки, я могу решить проблему с двумя совпадениями случаев
getWeather :: IO (ServiceResult WeatherService)
getWeather = do
...
let value = decode $ responseBody response
return $ case value of
Just v -> case (v ^? weatherService) of
Just wr -> Right wr
Nothing -> err "incompatible data"
Nothing -> err "bad json"
where
err t = Left (SimpleServiceError t)
weatherService :: Fold Value WeatherService
weatherService = runFold $ GetCityWeather
<$> Fold (key "name" . _String . unpacked)
<*> Fold (key "main" . key "temp" . _Double)