解码JSON流,其他值之前需要一些值

时间:2016-05-28 12:13:28

标签: haskell aeson haskell-pipes

让我们说我们有一个这样的JSON对象(使用base64编码的字节串):

TaggedImage = TaggedImage {  id :: Text, image :: ByteString }

现在,我们希望从来源收到image,并使用id标记中的信息将其存储在某个位置。因此,这意味着必须提前解析id(以确定图像的位置),同时以流方式解析image。这是直截了当的吗?

我打算使用pipes-aesonaws(用于S3存储)和pipesWebsocket制作人处使用{{1}进行流解码作为消费者的桶(在我们解析S3以确定id桶的位置之前,无法创建)。看看decoded方法,我无法弄清楚我是否确实可以做我上面提到的问题。这是我第一次尝试使用JSON和管道进行流式处理。所以,非常感谢帮助。

对文件系统进行读写操作的简单示例也可以作为S3Websocket producer的替代。

附录

由于在排序数组时,根据RFC JSON键值对是无序的,S3 consumer数据可能会出现在image之前,对于我在上面定义的数据类型。因此,它也可能有助于将其更改为JSON数组(Haskell中的一个元组id TH派生似乎转换为有序数组)。如果需要,请随意更改数据类型定义,以强加解码顺序。例如,数据类型可能更改为:

aeson

1 个答案:

答案 0 :(得分:1)

我相信你将无法重用pipes-aeson库,因为它没有提供一种流式传输已解码的JSON记录的嵌套字段的方法,也没有任何支持类似光标的导航结构。这意味着您需要手动解析JSON记录的骨架。

此外,还需要完成一些工作,将base64-bytestring包装在类似pipes的API中:

-- Convert a base64-encoded stream to a raw byte stream
decodeBase64
    :: Producer ByteString m r
    -- ^ Base64-encoded bytes
    -> Producer ByteString m (Either SomeException (Producer ByteString m r)) 
    -- ^ Raw bytes

请注意,如果解码成功完成,结果将为字节字符串的其余部分(即base64编码的字节后的所有内容)返回Producer。这使您可以继续解析图像字节结束的位置。

但是,假设您有一个decodeBase64函数,那么代码如何工作的大致轮廓就是您有三个部分:

  • 使用适合binary
  • pipes解析器在图像字节之前解析记录的前缀
  • 使用decodeBase64功能流式传输已解码的图像字节
  • 使用适用于binary
  • pipes解析器解析图像字节后的记录后缀

换句话说,类型和实现看起来大致如下:

-- This would match the "{ 'id' : 'foo', 'image' : '" prefix of the JSON record
skipPrefix :: Data.Binary.Get ()

skipPrefix’ :: Monad m => Producer ByteString m r -> m (Either DecodingError (Producer ByteString m r))
skipPrefix’ = execStateT (Pipes.Binary.decodeGet skipPrefix)

— This would match the "' }" suffix of the JSON record
skipSuffix :: Data.Binary.Get ()

skipSuffix’ :: Monad m => Producer ByteString m r -> m (Either DecodingError (Producer ByteString m r))
skipSuffix’ = execStateT (Pipes.Binary.decodeGet skipSuffix)

streamImage
    ::  Monad m
    =>  Producer ByteString m r
    ->  Producer ByteString m (Either SomeException (Producer ByteString m r))
streamImage p0 = do
    e0 <- lift (skipPrefix’ p0)
    case e0 of
        Left exc -> return (Left (toException exc))
        Right p1 -> do
            e1 <- decodeBase64 p1
            case e1 of
                Left exc -> return (Left exc)
                Right p2 -> do
                    e2 <- lift (skipSuffix’ p2)
                    case e2 of
                        Left exc -> return (Left (toException exc))
                        Right p3 -> return (Right p3)

换句话说,streamImage将以Producer作为输入,从JSON记录的第一个字符开始,它将流式传输从该记录中提取的解码图像字节。如果解码成功,那么它将在JSON记录之后立即返回字节流的剩余部分。