Подтвердить что ты не робот

Разбор JSON вписывается в запись в Haskell

Я пытаюсь понять это (я все еще немного новичок в Haskell), но я нахожу документацию для пакета Text.JSON немного запутанной. В основном у меня есть этот тип записи данных: -

data Tweet = Tweet
    {
        from_user :: String,
        to_user_id :: String,
        profile_image_url :: String,
        created_at :: String,
        id_str :: String,
        source :: String,
        to_user_id_str :: String,
        from_user_id_str :: String,
        from_user_id :: String,
        text :: String,
        metadata :: String
    }

и у меня есть некоторые твиты в формате JSON, которые соответствуют структуре этого типа. Вещь, с которой я борюсь, состоит в том, как сопоставить это выше с тем, что возвращается из следующего кода.

decode tweet :: Result JSValue

в указанный тип данных. Я понимаю, что я должен создать экземпляр instance JSON Tweet, но я не знаю, куда идти оттуда.

Любые указатели будут очень благодарны, спасибо!

4b9b3361

Ответ 1

Вам нужно написать метод showJSON и readJSON для вашего типа, который строит ваши значения Haskell из формата JSON. Пакет JSON позаботится о разборе сырой строки в JSValue для вас.

Ваш твит будет, скорее всего, JSObject, содержащий карту строк.

  • Используйте show для просмотра JSObject, чтобы увидеть, как выкладываются поля.
  • Вы можете искать каждое поле, используя get_field в JSObject.
  • Вы можете использовать fromJSString для получения регулярных строк Haskell из JSString.

В общем, вам понадобится что-то вроде

{-# LANGUAGE RecordWildCards #-}

import Text.JSON
import Text.JSON.Types

instance JSON Tweet where

    readJSON (JSObject o) = return $ Tweet { .. }
            where from_user         = grab o "from_user"
                  to_user_id        = grab o "to_user_id"
                  profile_image_url = grab o "proile_image_url"
                  created_at        = grab o "created_at"
                  id_str            = grab o "id_str"
                  source            = grab o "source"
                  to_user_id_str    = grab o "to_user_id_str"
                  from_user_id_str  = grab o "from_user_id_str"
                  from_user_id      = grab o "from_user_id"
                  text              = grab o "text"
                  metadata          = grab o "metadata"


grab o s = case get_field o s of
                Nothing            -> error "Invalid field " ++ show s
                Just (JSString s') -> fromJSString s'

Обратите внимание, что я использую довольно классное расширение языковых карт.

Без примера кодирования JSON я не могу больше советовать.


Связанные

Вы можете найти примеры экземпляров для кодировки JSON через экземпляры

  • в источнике, для простые типы. Или в других пакетах, которые зависят от json.
  • Пример для сообщений AUR здесь, как пример (низкий уровень).

Ответ 2

Я бы рекомендовал использовать новый aeson вместо json, поскольку первый выполняет намного лучше. Здесь, как вы конвертируете объект JSON в запись Haskell, используя aeson:

{-# LANGUAGE OverloadedStrings #-}
module Example where

import Control.Applicative
import Control.Monad
import Data.Aeson

data Tweet = Tweet {
    from_user :: String,
    to_user_id :: String,
    profile_image_url :: String,
    created_at :: String,
    id_str :: String,
    source :: String,
    to_user_id_str :: String,
    from_user_id_str :: String,
    from_user_id :: String,
    text :: String,
    metadata :: String
    }

instance FromJSON Tweet where
    parseJSON (Object v) =
        Tweet <$> v .: "from_user"
              <*> v .: "to_user_id"
              <*> v .: "profile_image_url"
              <*> v .: "created_at"
              <*> v .: "id_str"
              <*> v .: "source"
              <*> v .: "to_user_id_str"
              <*> v .: "from_user_id_str"
              <*> v .: "from_user_id"
              <*> v .: "text"
              <*> v .: "metadata"
    -- A non-Object value is of the wrong type, so use mzero to fail.
    parseJSON _          = mzero

Затем используйте Data.Aeson.json, чтобы получить парсер attoparsec, который преобразует a ByteString в Value. Вызов fromJSON на Value, чтобы попытаться проанализировать его в вашей записи. Обратите внимание, что в этих двух шагах присутствуют два разных парсера: парсер Data.Attoparsec.Parser для преобразования ByteString в общий JSON Value, а затем a Data.Aeson.Types.Parser парсер для преобразования значения JSON в запись. Обратите внимание, что оба этапа могут выйти из строя:

  • Первый парсер может выйти из строя, если ByteString не является допустимым значением JSON.
  • Второй парсер может выйти из строя, если значение (действительное) JSON не содержит одно из полей, упомянутых в вашей реализации fromJSON.

Пакет aeson предпочитает новый тип Unicode Text (определенный в пакете text) в более старую школу String тип. Тип Text имеет гораздо более эффективное представление памяти, чем String и, как правило, работает лучше. Я рекомендую вам изменить тип Tweet, чтобы использовать Text вместо String.

Если вам когда-либо понадобится конвертировать между String и Text, используйте функции pack и unpack, определенные в Data.Text. Обратите внимание, что для таких преобразований требуется время O (n), поэтому избегайте их как можно больше (т.е. Всегда используйте Text).

Ответ 3

Импортируйте Data.JSon.Generic и Data.Data, затем добавьте вывод (данные) в свой тип записи, а затем попробуйте использовать decodeJSON в твитте.

Ответ 5

Я поддерживаю ответ @tibbe. Тем не менее, я хотел бы добавить, как вы проверяете, поставить какое-то значение по умолчанию на случай, если аргумент отсутствует в JSON.

В ответе tibbe вы можете сделать следующее:

Tweet <$> v .: "from_user"
      <*> v .:? "to_user_id"       .!= "some user here"
      <*> v .: "profile_image_url" .!= "url to image"
      <*> v .: "created_at"
      <*> v .: "id_str"             != 232131
      <*> v .: "source"

это будут параметры по умолчанию, которые будут приняты при анализе JSON.