Разбор 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
, но я не знаю, куда идти оттуда.
Любые указатели будут очень благодарны, спасибо!
Ответы
Ответ 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 в твитте.
Ответ 4
Вот пример Text.JSON.Generic
и decodeJSON
: http://hpaste.org/41263/parsing_json_with_textjson
Ответ 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.