从SQL数据库反序列化数据 [英] Deserializing data form a SQL Database

查看:411
本文介绍了从SQL数据库反序列化数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个由数据库支持的小应用程序(SQLite,但与问题无关).我定义了一些类型,例如:

I've a little application, backed by a database (SQLite, but it's not really relevant to the question). I've defined some types like:

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

类型被映射到数据库中的表.当我读取数据时,最终会写出这样的函数:

The types are mapped to tables in the DB. When I read the data, I end up writing functions like this one:

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

(为了清楚起见,我省略了处理错误.)

(I'm ommiting handling errors for the sake of clarity.)

像这样的编写功能确实很烦人,感觉就像创建了很多样板.是否有更惯用的方式将一组SqlValues转换为Haskell数据?

Writing functions like these is really annoing and feels like creating a lot of boilerplate. Is there a more idiomatic way to convert a group of SqlValues into a Haskell data?

推荐答案

HDBC库中似乎没有任何标准方法.如果您特别热衷,可以使用 GHC.Generics 为此,尽管治愈可能比疾病还差!

There doesn't seem to be any standard way in the HDBC library for this. If you're feeling particularly keen, you can roll your own machinery with GHC.Generics for this, though the cure may be worse than the disease!

我还添加了逆向转换,但是您可以根据需要省略/拆分类:

I also added the reverse conversion, but you can leave that out/split the classes if you want:

{-# 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 = _
-}

这篇关于从SQL数据库反序列化数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆