簡體   English   中英

從SQL數據庫反序列化數據

[英]Deserializing data form a SQL Database

我有一個小應用程序,由數據庫支持(SQLite,但它與問題無關)。 我已經定義了一些類型:

data Whatever = Whatever Int Int String String
data ImportantStuff = ImportantStuff { id :: Int, count :: Int, name :: String, description :: String }

類型映射到DB中的表。 當我讀取數據時,我最終編寫了這樣的函數:

whateverFromDB :: [SqlValue] -> Whatever
whateverFromDB (a:b:c:d:_) = Whatever (fromSql a) (fromSql b) (fromSql c) (fromSql d)

(為了清楚起見,我省略了處理錯誤。)

像這樣的編寫函數真的很煩人,感覺就像創建了很多樣板。 是否有更慣用的方法將一組SqlValues轉換為Haskell數據?

HDBC庫中似乎沒有任何標准方法。 如果您感覺特別敏銳,可以使用GHC.Generics為此推出自己的機器,盡管治愈可能比疾病更糟糕!

我還添加了反向轉換,但如果您願意,可以將其保留/拆分:

{-# LANGUAGE DeriveAnyClass, DeriveGeneric, DefaultSignatures
           , TypeOperators, FlexibleContexts, FlexibleInstances
           , TypeSynonymInstances #-}

import Data.Convertible
import Database.HDBC


import Data.Coercible -- not strictly necessary
import GHC.Generics

-- serialization for Generic Rep-resentations
class GSqlConvert f where
    gFromSqlValuesImpl :: [SqlValue] -> (f a, [SqlValue])
    gToSqlValuesImpl :: f a -> [SqlValue] -> [SqlValue]

-- no data, no representation
instance GSqlConvert U1 where
    gFromSqlValuesImpl vs = (U1, vs)
    gToSqlValuesImpl U1 vs = vs

-- multiple things are stored in order
instance (GSqlConvert a, GSqlConvert b) => GSqlConvert (a :*: b) where
    gFromSqlValuesImpl vs =
        let (a, vs1) = gFromSqlValuesImpl vs
            (b, vs2) = gFromSqlValuesImpl vs1
         in (a :*: b, vs2)
    gToSqlValuesImpl (a :*: b) = gToSqlValuesImpl a . gToSqlValuesImpl b

-- note no instance for a :+: b, so no support for unions

-- ignore metadata
instance GSqlConvert a => GSqlConvert (M1 i c a) where
    gFromSqlValuesImpl = coerce . gFromSqlValuesImpl
    gToSqlValuesImpl = gToSqlValuesImpl . unM1

-- delegate to the members' serializers
instance SqlConvert a => GSqlConvert (K1 i a) where
    gFromSqlValuesImpl = coerce . fromSqlValuesImpl
    gToSqlValuesImpl = toSqlValuesImpl . unK1

-- serialization for normal data types
-- some types are "primitive" and have their own serialization code
-- other types are serialized via the default implementations,
-- which are based on Generic
-- the defaults convert the data into a generic representation and let
-- the GSqlConvert class decide how to serialize the generic representation
class SqlConvert a where
    fromSqlValuesImpl :: [SqlValue] -> (a, [SqlValue])
    default fromSqlValuesImpl :: (Generic a, GSqlConvert (Rep a))
                              => [SqlValue] -> (a, [SqlValue])
    fromSqlValuesImpl vs =
        let (rep, vs1) = gFromSqlValuesImpl vs
         in (to rep, vs1)

    toSqlValuesImpl :: a -> [SqlValue] -> [SqlValue]
    default toSqlValuesImpl :: (Generic a, GSqlConvert (Rep a))
                            => a -> [SqlValue] -> [SqlValue]
    toSqlValuesImpl a vs = gToSqlValuesImpl (from a) vs

fromSqlValuesImplPrim :: Convertible SqlValue a
                      => [SqlValue] -> (a, [SqlValue])
-- no error checking
fromSqlValuesImplPrim (v:vs) = (fromSql v, vs)

toSqlValuesImplPrim :: Convertible a SqlValue
                    => a -> [SqlValue] -> [SqlValue]
toSqlValuesImplPrim a vs = toSql a:vs

instance SqlConvert Int where
    fromSqlValuesImpl = fromSqlValuesImplPrim
    toSqlValuesImpl = toSqlValuesImplPrim
instance SqlConvert String where
    fromSqlValuesImpl = fromSqlValuesImplPrim
    toSqlValuesImpl = toSqlValuesImplPrim

fromSqlValues :: SqlConvert t => [SqlValue] -> t
 -- no error checking for unused values
fromSqlValues = fst . fromSqlValuesImpl

toSqlValues :: SqlConvert t => t -> [SqlValue]
toSqlValues v = toSqlValuesImpl v []

-- and now given all the above machinery, the conversion
-- for Whatever comes for free:
data Whatever = Whatever Int Int String String
    deriving (Show, Generic, SqlConvert)

{-
-- DeriveGeneric produces:
instance Generic Whatever where
  type Rep Whatever = D1 _ (C1 _ (
                            (S1 _ (Rec0 Int) :*: S1 _ (Rec0 Int))
                        :*: (S1 _ (Rec0 String) :*: S1 _ (Rec0 String))
                      ))
  to = _; from = _
-- There is an instance for GSqlConvert (Rep Whatever)
-- DeriveAnyClass produces
instance SqlConvert Whatever where
-- DefaultSignatures uses the default implementations from the class declaration
-- to implement the methods
   fromSqlValuesImpl = _; toSqlValuesImpl = _
-}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM