Как я могу преобразовать объект Aeson в свой собственный тип?

Я пытаюсь написать парсер JSON с помощью 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 определенно соответствует 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, я получаю список всех Ничего.

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)

Я смог написать экземпляр ToJSON для WeatherObj, но это оказалось неуместным, потому что мне нужно преобразовать общий Object в WeatherObj. Я считаю, что мне нужна функция decode, хотя я могу ошибаться.


person Nicholas Hubbard    schedule 19.07.2020    source источник


Ответы (1)


Дано:

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

И получите объект Maybe WeatherObj. Написав свой собственный экземпляр FromJSON, я думаю, вы сделали свою жизнь немного сложнее, чем нужно.

person Alain O'Dea    schedule 19.07.2020