如何将 Aeson 对象解析为我自己的自定义类型?
How can I parse an Aeson Object into my own custom type?
我正在尝试用 Aeson 编写 JSON 解析器。
我在代码中调用 JSON 的方式:
testReq :: Request
testReq = parseRequest_ "https://api.openweathermap.org/data/2.5/onecall?lat=41.63526&lon=-70.92701&exclude=minutely&appid=93120a85abf28f8fb1cdae14ffd7435d&units=metric"
首先我定义我的自定义类型
type Celsius = Double
type HPA = Int --Hectopascal Pressure Unit
type Percent = Int
type Meter = Int
type MeterPerSec = Double
type CompassDegree = Int
data WeatherObj =
WeatherObj
{ time :: UTCTime
, temp :: Celsius
, feels_like :: Celsius
, pressure :: HPA
, humidity :: Percent
, visibility :: Meter
, wind_speed :: MeterPerSec
, wind_deg :: CompassDegree
}
deriving (Eq, Show, Generic)
接下来我写我的 FromJSON 实例,我知道它是有效的,因为如果我 运行 parseCurrentWeather testReq
我回来 WeatherObj {time = 2020-07-19 16:54:43 UTC, temp = 25.51, feels_like = 29.49, pressure = 1012, humidity = 83, visibility = 10000, wind_speed = 1.34, wind_deg = 247}
这太完美了!
instance FromJSON WeatherObj where
parseJSON = withObject "weatherObj" $ \obj -> do
timeOffset <- obj .: "timezone_offset"
currentO <- obj .: "current"
dt <- currentO .: "dt"
temp <- currentO .: "temp"
feels_like <- currentO .: "feels_like"
pressure <- currentO .: "pressure"
humidity <- currentO .: "humidity"
visibility <- currentO .: "visibility"
wind_speed <- currentO .: "wind_speed"
wind_deg <- currentO .: "wind_deg"
pure $ WeatherObj (makeLocalTime dt timeOffset)
temp feels_like
pressure humidity
visibility wind_speed
wind_deg
parseCurrentWeather :: Request -> IO WeatherObj
parseCurrentWeather req = do
current <- fetchCurrentWeather req
pure $ getResponseBody current
现在我需要弄清楚如何解析应该返回 48 个对象的每小时天气。当我 运行 parseHourly testReq
返回一长串 JSON 时,此代码无一例外地工作。这个 JSON 绝对匹配 link 中的 JSON。到目前为止,我看起来很棒。
fetchHourly :: Request -> IO (Response HourlyWeathers) --Could also be IO (Response Object)
fetchHourly = httpJSON
data HourlyWeathers =
HourlyWeathers
{ getHours :: [Object] }
deriving (Eq, Show, Generic)
instance FromJSON HourlyWeathers where
parseJSON = withObject "hourlyWeather" $ \obj -> do
allHours <- obj .: "hourly"
pure $ HourlyWeathers allHours
parseHourly :: Request -> IO HourlyWeathers
parseHourly req = do
hours <- fetchHourly req
pure $ getResponseBody hours
现在我们到了有问题的代码处。我想将 objToWeatherObj
映射到我用 parseHourly
生成的对象列表中。我似乎无法克服的问题是,当我 运行 parseHourlyObjects
时,我得到了所有 Nothings 的列表。
parseHourlyObjects :: Request -> IO [Maybe WeatherObj]
parseHourlyObjects req = do
hourly <- fetchHourly req
let x = getHours $ getResponseBody hourly
y = fmap objToWeatherObj x
pure y
objToWeatherObj :: Object -> Maybe WeatherObj
objToWeatherObj = (decode . encode)
我已经能够为 WeatherObj
编写一个 ToJSON
实例,但事实证明这无关紧要,因为我需要将通用 Object
解析为 WeatherObj
.我相信我在这里需要的功能是decode
,虽然我可能是错的。
鉴于:
data WeatherObj =
WeatherObj
{ time :: UTCTime
, temp :: Celsius
, feels_like :: Celsius
, pressure :: HPA
, humidity :: Percent
, visibility :: Meter
, wind_speed :: MeterPerSec
, wind_deg :: CompassDegree
}
deriving (Eq, Show, Generic, FromJSON)
请注意,它现在也从 FromJSON 派生。
您可以:
decode "{\"time\":\"...\",...}" :: Maybe WeatherObj
并获得 也许是 WeatherObj。通过编写您自己的 FromJSON 实例,我认为您的生活可能比需要的更具挑战性。
我正在尝试用 Aeson 编写 JSON 解析器。
我在代码中调用 JSON 的方式:
testReq :: Request
testReq = parseRequest_ "https://api.openweathermap.org/data/2.5/onecall?lat=41.63526&lon=-70.92701&exclude=minutely&appid=93120a85abf28f8fb1cdae14ffd7435d&units=metric"
首先我定义我的自定义类型
type Celsius = Double
type HPA = Int --Hectopascal Pressure Unit
type Percent = Int
type Meter = Int
type MeterPerSec = Double
type CompassDegree = Int
data WeatherObj =
WeatherObj
{ time :: UTCTime
, temp :: Celsius
, feels_like :: Celsius
, pressure :: HPA
, humidity :: Percent
, visibility :: Meter
, wind_speed :: MeterPerSec
, wind_deg :: CompassDegree
}
deriving (Eq, Show, Generic)
接下来我写我的 FromJSON 实例,我知道它是有效的,因为如果我 运行 parseCurrentWeather testReq
我回来 WeatherObj {time = 2020-07-19 16:54:43 UTC, temp = 25.51, feels_like = 29.49, pressure = 1012, humidity = 83, visibility = 10000, wind_speed = 1.34, wind_deg = 247}
这太完美了!
instance FromJSON WeatherObj where
parseJSON = withObject "weatherObj" $ \obj -> do
timeOffset <- obj .: "timezone_offset"
currentO <- obj .: "current"
dt <- currentO .: "dt"
temp <- currentO .: "temp"
feels_like <- currentO .: "feels_like"
pressure <- currentO .: "pressure"
humidity <- currentO .: "humidity"
visibility <- currentO .: "visibility"
wind_speed <- currentO .: "wind_speed"
wind_deg <- currentO .: "wind_deg"
pure $ WeatherObj (makeLocalTime dt timeOffset)
temp feels_like
pressure humidity
visibility wind_speed
wind_deg
parseCurrentWeather :: Request -> IO WeatherObj
parseCurrentWeather req = do
current <- fetchCurrentWeather req
pure $ getResponseBody current
现在我需要弄清楚如何解析应该返回 48 个对象的每小时天气。当我 运行 parseHourly testReq
返回一长串 JSON 时,此代码无一例外地工作。这个 JSON 绝对匹配 link 中的 JSON。到目前为止,我看起来很棒。
fetchHourly :: Request -> IO (Response HourlyWeathers) --Could also be IO (Response Object)
fetchHourly = httpJSON
data HourlyWeathers =
HourlyWeathers
{ getHours :: [Object] }
deriving (Eq, Show, Generic)
instance FromJSON HourlyWeathers where
parseJSON = withObject "hourlyWeather" $ \obj -> do
allHours <- obj .: "hourly"
pure $ HourlyWeathers allHours
parseHourly :: Request -> IO HourlyWeathers
parseHourly req = do
hours <- fetchHourly req
pure $ getResponseBody hours
现在我们到了有问题的代码处。我想将 objToWeatherObj
映射到我用 parseHourly
生成的对象列表中。我似乎无法克服的问题是,当我 运行 parseHourlyObjects
时,我得到了所有 Nothings 的列表。
parseHourlyObjects :: Request -> IO [Maybe WeatherObj]
parseHourlyObjects req = do
hourly <- fetchHourly req
let x = getHours $ getResponseBody hourly
y = fmap objToWeatherObj x
pure y
objToWeatherObj :: Object -> Maybe WeatherObj
objToWeatherObj = (decode . encode)
我已经能够为 WeatherObj
编写一个 ToJSON
实例,但事实证明这无关紧要,因为我需要将通用 Object
解析为 WeatherObj
.我相信我在这里需要的功能是decode
,虽然我可能是错的。
鉴于:
data WeatherObj =
WeatherObj
{ time :: UTCTime
, temp :: Celsius
, feels_like :: Celsius
, pressure :: HPA
, humidity :: Percent
, visibility :: Meter
, wind_speed :: MeterPerSec
, wind_deg :: CompassDegree
}
deriving (Eq, Show, Generic, FromJSON)
请注意,它现在也从 FromJSON 派生。
您可以:
decode "{\"time\":\"...\",...}" :: Maybe WeatherObj
并获得 也许是 WeatherObj。通过编写您自己的 FromJSON 实例,我认为您的生活可能比需要的更具挑战性。