Разбор JSON-строки в запись в Haskell - PullRequest
19 голосов
/ 23 мая 2011

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

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

Ответы [ 5 ]

25 голосов
/ 23 мая 2011

Я бы порекомендовал использовать новый пакет 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 парсер, который преобразует ByteString в Value. Позвоните fromJSON на Value, чтобы попытаться проанализировать его в вашей записи. Обратите внимание, что в этих двух шагах участвуют два различных синтаксических анализатора: синтаксический анализатор Data.Attoparsec.Parser для преобразования ByteString в общий JSON Value и затем анализатор 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).

14 голосов
/ 23 мая 2011

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

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

  • Используйте show, чтобы посмотреть на объект JSO, чтобы увидеть, как расположены поля.
  • Вы можете искать каждое поле, используя 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 находится здесь , в качестве примера (низкого уровня).
5 голосов
/ 23 мая 2011

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

1 голос
/ 12 июля 2019

Я поддерживаю ответ @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"

thisбудут ли параметры по умолчанию приниматься при анализе JSON.

0 голосов
/ 14 марта 2013

Вот пример Text.JSON.Generic и decodeJSON: http://hpaste.org/41263/parsing_json_with_textjson

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...