当前位置: 首页 > 面试题库 >

从SQL数据库反序列化数据

张积厚
2023-03-14
问题内容

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

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

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

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


 类似资料:
  • 问题内容: 我正在使用python访问由Drupal安装管理的数据库。我想在Drupal中访问的数据作为PHP序列化对象保存在数据库中。 是否有任何预构建的python模块可以将PHP序列化对象反序列化为python对象?我已经做了一些搜索,却一无所获。 我意识到我可以从头开始编写自己的解析器,但是我宁愿使用经过尝试和测试的东西。 问题答案: 您在寻找phpserialize吗?

  • 我需要将下面两个foramt反序列化为单个代码中的 嵌套异常是org.springframework.http.converter.httpmessageNotreadableException:JSON解析错误:无法反序列化字符串类型的值“2020-04-23t10:51:24.238+01:00”:com.fasterxml.jackson.databind.exc.invalidformat

  • 数据序列化在 Redisson 中广泛地用于解编排在 Redis 服务器连接的网络上接收和发送的字节。 默认有多种流行的解编码器可用: Codec 类名 描述 org.redisson.codec.JsonJacksonCodec Jackson JSON codec. 默认 codec org.redisson.codec.CborJacksonCodec CBOR 二进制 json codec

  • 什么是数据序列化(Data Serialization)? 数据序列化(Data Serialization)用于将结构化数据转换为允许被分享或存储的格式,并且通过这种方式,数据的原始结构可以被恢复。在某些情况下,数据序列化的另一个次要目的是将序列化数据的尺寸最小化,从而最小化其对硬盘空间或带宽的要求。 Pickle Python 原生的数据序列化模块叫做 Pickle。 这里举个例子: impo

  • JDK 提供了 ObjectOutputStream 和 ObjectInputStream 通过网络将原始数据类型和 POJO 进行序列化和反序列化。API并不复杂,可以应用到任何对象,支持 java.io.Serializable 接口。但它也不是非常高效的。在本节中,我们将看到 Netty 所提供的。 JDK 序列化 如果程序与端对端间的交互是使用 ObjectOutputStream 和

  • 我应该能够对配对到JSON/从JSON配对。 我有