Haskell.Драйвер MongoDB или проблема с кодировкой Aeson - PullRequest
2 голосов
/ 26 сентября 2011

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

{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.Wai.Handler.Warp (run)
import Data.Enumerator (Iteratee (..))
import Data.Either (either)
import Control.Monad (join)
import Data.Maybe (fromMaybe)
import Network.HTTP.Types (statusOK, status404)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.ByteString.Char8 (unpack)
import Data.ByteString.Lazy.Char8 (pack)
import qualified Data.Text.Lazy as T
import Data.Text (Text(..))
import Control.Monad.IO.Class (liftIO, MonadIO)
import Data.Aeson (encode)
import qualified Data.Map as Map
import qualified Database.MongoDB as DB

application dbpipe req = do
  case unpack $ rawPathInfo req of
    "/items" -> itemsJSON dbpipe req
    _ ->  return $ responseLBS status404 [("Content-Type", "text/plain")] "404"

indexPage :: Iteratee B.ByteString IO Response
indexPage = do
  page <- liftIO $ processTemplate "templates/index.html" []
  return $ responseLBS statusOK [("Content-Type", "text/html; charset=utf-8")] page

processTemplate f attrs = do
  page <- L.readFile f
  return page

itemsJSON :: DB.Pipe -> Request -> Iteratee B.ByteString IO Response
itemsJSON dbpipe req = do
  dbresult <- liftIO $  rundb dbpipe $ DB.find (DB.select [] $ tu "table") >>= DB.rest
  let docs = either (const []) id dbresult
--  liftIO $ L.putStrLn $ encode $ show $ map docToMap docs
  return $ responseLBS statusOK [("Content-Type", "text/plain; charset=utf-8")]
    (encode $ map docToMap docs)

docToMap doc = Map.fromList $ map (\f -> (T.dropAround (== '"') $ T.pack $ show $ DB.label f, T.dropAround (== '"') $ T.pack $ show $ DB.value f)) doc


main =  do
  pipe <- DB.runIOE $ DB.connect $ DB.host "127.0.0.1"
  run 3000 $ application pipe

rundb pipe act = DB.access pipe DB.master database act

tu :: B.ByteString -> UString
tu = DB.u . C8.unpack

Тогда результат удивителен, DB.labelработает хорошо, но DB.value дает мне нативные символы в качестве некоторых escape-кодов, поэтому результат выглядит так:

curl <a href="http://localhost:3000/items" rel="nofollow">http://localhost:3000/items</a> дает:

[{"Марка": "\1058\1080\1087 \1087\1086\1076",
  "Model": "BD-W LG BP06LU10 Slim \1058\1080\1087 \1087\1086\1076\1082\1083\1102\1095\1077\1085\1080\1103"},
 ...
]

Это происходит в случае, если я пытаюсьпечатать данные, а также в случае, если я возвращаю данные, закодированные как JSON. Есть идеи, как правильно извлечь значения из драйвера MongoDB?

Ответы [ 2 ]

1 голос
/ 27 сентября 2011

Следующая строка подтверждает, что кодирование aeson работает правильно (использование библиотеки utf8-строк для чтения данных utf8 из ленивой строки тестирования обратно в строку haskell:

> putStrLn $ Data.ByteString.Lazy.UTF8.toString $ encode $ ("\1058\1080\1087 \1087\1086\1076",12)
["Тип под",12]

При более внимательном рассмотрении вашего кода я вижуреальная проблема. Вы звоните T.pack $ show $ DB.value - это будет отображаться как буквальные кодовые точки, а затем упаковывать их в текстовый объект. Исправление заключается в переключении с шоу на что-то более умное. Посмотрите на это (не проверено)

smartShow :: DB.Value -> Text
smartShow (String s) = Data.Text.Encoding.decodeUtf8 $ Data.CompactString.UTF8.toByteString s
smartShow x = T.pack $ show x

Очевидно, что для обработки рекурсивных случаев и т. Д. Вам нужно быть умнее этого, но это общее понятие ...

На самом деле, "лучшее", что нужно сделать, это написатьфункция BSON -> JSON напрямую, а не проходить через какие-либо промежуточные структуры вообще.

1 голос
/ 26 сентября 2011

Все работает, как ожидалось - только ваши ожидания не соответствуют действительности.=)

То, что вы видите, не является необработанным String s;они String, которым удалось избежать существования исключительно в области ASCII для печати с помощью функции show, вызываемой print:

print = putStrLn . show

Никогда не бойтесь: в памяти строка,print s, как "\1058" на самом деле представляет собой одну длинную кодовую точку Unicode.Вы можете наблюдать это, напечатав длину одного из String, который вас интересует, и сравнив его с ожидаемым количеством кодовых точек Unicode.

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