Разбор Haskel JSON с Aeson - PullRequest
       7

Разбор Haskel JSON с Aeson

0 голосов
/ 28 апреля 2018

У меня есть источник данных JSON, который выглядит следующим образом:

{ "fields": [
{ "type": "datetime",
  "name": "Observation Valid",
  "description": "Observation Valid Time"},
{ "type": "datetime",
  "name": "Observation Valid UTC",
  "description": "Observation Valid Time UTC"},
{ "type": "number",
  "name": "Air Temperature[F]",
  "description": "Air Temperature at 2m AGL"},
{ "type": "number",
  "name": "Wind Speed[kt]",
  "description": "Wind Speed"},
{ "type": "number",
  "name": "Wind Gust[kt]",
  "description": "Wind Gust"},
{ "type": "number", "name":
  "Wind Direction[deg]",
  "description": "Wind Direction"}
  ],
"rows": [
["2018-04-22T00:10:00", "2018-04-22T05:10:00Z", 50.0, 9.0, null, 50.0],
["2018-04-22T00:15:00", "2018-04-22T05:15:00Z", 50.0, 9.0, null, 60.0],
["2018-04-22T00:20:00", "2018-04-22T05:20:00Z", 50.0, 8.0, null, 60.0],
["2018-04-22T00:30:00", "2018-04-22T05:30:00Z", 50.0, 9.0, null, 60.0]
]
}

            ( https://mesonet.agron.iastate.edu/json/obhistory.py?station=TVK&network=AWOS&date=2018-04-22 )

И попробовал несколько описаний данных, наконец, это:

data Entry =             -- Data entries
  Entry {  time      ::  Text     -- Observation Valid Time
         , timeUTC   ::  Text     -- Observation Valid Time UTC
         , airTemp   ::  Float    -- Air Temperature[F] at 2m AGL
         , wind      ::  Float    -- Wind Speed [kt]
         , gust      ::  Float    -- Wind Gust [kt]
         , direction ::  Int      -- Wind Direction[deg]
           } deriving (Show,Generic)

data Field =             -- Schema Definition
  Field {  ftype       :: String     -- 
         , name        :: String     -- 
         , description :: String    -- 
           } deriving (Show,Generic)

data Record =
  Record {  fields  :: [Field]     -- 
          , rows    :: [Entry]     -- data
           } deriving (Show,Generic)

-- Instances to convert our type to/from JSON.
instance FromJSON Entry
instance FromJSON Field
instance FromJSON Record

-- Get JSON data and decode it
dat <- (eitherDecode <$> getJSON) :: IO (Either String Record)

, который дает эту ошибку: Ошибка в $ .fields [0]: ключ "ftype" отсутствует

(Первая) ошибка связана с определениями полей (которые я не использую). В JSON Entry - это массивы смешанных типов, но в Haskell это просто структура данных, а не массив - не уверен, как их согласовать.

Без сомнения, ошибка новичка, но я не нашел ни одного примера, который, похоже, имеет такую ​​структуру. Нужно ли для этого написать собственный парсер?

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

Три вещи мешают этому работать должным образом:

  • Данные JSON содержат поле с именем "type". Пользовательский экземпляр FromJson для типа записи Field может справиться с этим.
  • Данные типа Entry являются безымянными, поэтому их лучше представить в виде записи data без имен полей или tuple.
  • Float, представляющий порыв ветра, иногда null, поэтому это должно быть Maybe Float

Приведенный ниже код содержит все эти модификации и анализирует данные вашего примера JSON:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

import Data.ByteString.Lazy as BSL
import Data.Text (Text)
import Data.Aeson 
import GHC.Generics

-- Either this tuple definition of Entry or the data definition without
-- names (commented out) will work.  

type Entry =             -- Data entries
  ( Text     -- Observation Valid Time
  , Text     -- Observation Valid Time UTC
  , Float    -- Air Temperature[F] at 2m AGL
  , Float    -- Wind Speed [kt]
  , Maybe Float    -- Wind Gust [kt]
  , Int      -- Wind Direction[deg]
  ) 

-- data Entry =             -- Data entries
--   Entry Text     -- Observation Valid Time
--         Text     -- Observation Valid Time UTC
--         Float    -- Air Temperature[F] at 2m AGL
--         Float    -- Wind Speed [kt]
--         (Maybe Float)    -- Wind Gust [kt]
--         Int      -- Wind Direction[deg]
--         deriving (Show,Generic)

-- instance FromJSON Entry

data Field =             -- Schema Definition
  Field {  ftype       :: String    -- 
        ,  name        :: String    -- 
        ,  description :: String    -- 
        } deriving (Show,Generic)

instance FromJSON Field where
  parseJSON = withObject "Field" $ \v -> Field
    <$> v .: "type"
    <*> v .: "name"
    <*> v .: "description"

data Record =
  Record {  fields  :: [Field]     -- 
         ,  rows    :: [Entry]     -- data
         } deriving (Show,Generic)

instance FromJSON Record

getJSON :: IO ByteString
getJSON = BSL.readFile "json.txt" 

main :: IO()
main = do
  -- Get JSON data and decode it
  dat <- (eitherDecode <$> getJSON) :: IO (Either String Record)
  case dat of
    Right parsed -> print parsed
    Left err -> print err
0 голосов
/ 28 апреля 2018
Поле

имеет поле ftype, поэтому AESON пытается найти ftype в JSON, но не может (так как он содержит ftype). Я понимаю, что вы не можете назвать поле type в Haskell, поэтому вам нужно найти способ заставить AESON использовать другое имя. Вам нужно использовать шаблон Haskell и соответственно установить fieldLabelModifier. Кроме того, написание настойчивости вручную может быть проще.

...