haskell - 我们如何将 Haskell 数据结构序列化和反序列化为字节串?
问题描述
我有一个用 C++ 编写的 TCP 服务器,它需要以下格式的请求:
struct Header {
int headerField1;
int headerField2;
}
struct Request {
Header header;
char[14] uniqueID;
char[12] password;
}
我想实现一个客户端将此请求发送到我在 Haskell 中的服务器
我已经尝试过 Data.Binary.encode 没有成功。我更困惑如何在haskell中使用任意大小的类型。即字符[12];
哈斯克尔代码:
data Header = Header
{
headerField1 :: Word32
, headerField2 :: Word32
} deriving (Generic)
instance Binary Header
data Request = Request
{
header :: Header
, uniqueID :: ByteString -- I am not sure which data type to use here.
, password :: ByteString -- Same as above, as length is defined 12 bytes which is arbitrary.
} deriving (Generic)
instance Binary Request
我已经为数据解析器编写了一个自定义字节串,它非常适合标头,因为没有任意大小的类型
parseHeader = do
Header <$>
getWord32le <*>
getWord32le
我正在寻找一种 Haskell 方法来对定义为 ByteString 的数据包结构进行序列化和反序列化(反之亦然),以及一种创建任意大小数据类型的方法——char [12]
解决方案
getByteString
要首先解决主要问题,您可以使用(或getLazyByteString
)解析已知长度的字节串。所以二进制解析器Request
可能是:
parseRequest :: Get Request
parseRequest =
Request
<$> parseHeader
<*> getByteString 14
<*> getByteString 12
如果你也有一个序列化器,比如说putRequest
,你可以把它放在一个Binary
带有解析器的实例中,为了方便起见,你可以使用库的更多功能(但你不必这样做)。
instance Binary Request where
get = parseRequest
put = putRequest
为避免混淆密码和 ID,将它们包装在新类型中似乎是个好主意:
newtype UniqueID = MkUniqueID ByteString -- length 14
newtype Password = MkPassword ByteString -- length 12
在对它们执行操作时,请确保它们不会构造错误长度的值。然后您可以在导出类型时隐藏构造函数,以便用户无法破坏这些不变量。
这些类型的解析器是您指定所需长度的地方:
parseUniqueID :: Get UniqueID
parseUniqueID = MkUniqueID <$> getByteString 14
parsePassword :: Get Password
parsePassword = MkPassword <$> getByteString 12
现在这使得定义Request
更具描述性,在 Haskell 代码中混合密码和 ID 的唯一方法是在序列化/反序列化中弄错顺序,因此这减少了其他地方出错的可能性。
data Request = Request
{ header :: Header
, uniqueID :: UniqueID
, password :: Password
}
parseRequest :: Get Request
parseRequest =
Request
<$> parseHeader
<*> parseUniqueID
<*> parsePassword
推荐阅读
- react-native - 跟踪来自 Facebook 和 Instagram 的应用下载
- maven - compile和provided有什么区别
- python - 内联python脚本的命令行参数?
- wordpress - 易受攻击的java脚本库
- python - BaseCommand 用于通过解析器中的参数从特定模型中删除数据
- excel - excel VBA的总计数据透视表范围参考
- ckeditor - 如何一次获取多个选定元素
- python - 修补包内的函数 __init__ 并在同一包内的模块中使用它
- python - python中str(77)和int(77)有什么区别
- c# - 如何将此查询转换为 linq?