在我当前的“ learning haskell”项目中,我尝试从第三方api获取天气数据。我想从以下响应正文中提取name
和main.temp
值:
{
...
"main": {
"temp": 280.32,
...
},
...
"name": "London",
...
}
我编写了一个getWeather
服务来执行IO并将响应转换为构造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)
我坚持在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)
答案 0 :(得分:1)
@jpath指出,这里真正的问题是关于lens
和JSON处理的问题。问题的症结似乎是您想一次完成镜头操作。为此,请查看方便的ReifiedFold
:您想要的“并行”功能已打包到Applicative
实例中。
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import Data.Text.Lens ( unpacked )
-- | Extract a `WeatherService` from a `Value` if possible
weatherService :: Fold Value WeatherService
weatherService = runFold $ GetCityWeather
<$> Fold (key "name" . _String . unpacked)
<*> Fold (key "main" . key "temp" . _Double))
然后,您可以尝试一次获取WeatherService
:
...
-- work thru the response
let body = responseBody r
return $ case body ^? weatherService of
Just wr -> Right wr
Nothing -> Left (SimpleServiceError ("borked data >:( " ++ show body))
但是,出于错误消息的考虑,如果您打算进一步扩展,最好利用aeson
的{{1}} / ToJSON
。