哈斯克尔。 MongoDB驱动程序或Aeson charset问题

时间:2011-09-26 10:41:51

标签: haskell mongodb

美好的一天,我有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给我原生字符作为一些转义码,所以结果如下:

curl http://localhost:3000/items给出:

[{"Марка": "\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 个答案:

答案 0 :(得分:1)

一切都按预期工作 - 只有你的期望是错误的。 =)

你所看到的不是原始的String;它们是String,它们被show函数转义为纯粹在可打印的ASCII范围内,由print调用:

print = putStrLn . show

永远不要害怕:在内存中,print作为"\1058"的字符串实际上是一个单一的Unicode代码点。您可以通过打印您感兴趣的String之一的长度并将其与您期望的Unicode代码点数进行比较来观察此情况。

答案 1 :(得分:1)

以下行确认aeson的编码正常工作(使用utf8-string库将惰性字节串中的utf8数据读回haskell字符串:

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

更仔细地查看您的代码,我看到了真正的问题。您正在调用T.pack $ show $ DB.value - 这将呈现为文字代码点,然后将其打包到文本对象中。修复是从show切换到更智能的东西。看看这个(未经测试的)

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

显然要处理递归案例等,你需要比这更聪明,但这是一般概念...

事实上,“最好的”事情是直接编写BSON -> JSON函数,而不是完全通过任何中间结构。