connecting http-conduit to xml-conduit

2019-05-07 11:47发布

I'm struggling converting a Response from http-conduit to an XML document via xml-conduit.

The doPost function takes an XML Document and posts it to the server. The server responds with an XML Document.

doPost queryDoc = do
    runResourceT $ do

        manager <- liftIO $ newManager def
        req <- liftIO $ parseUrl hostname

        let req2 = req
                { method = H.methodPost
                , requestHeaders = [(CI.mk $ fromString "Content-Type", fromString "text/xml" :: Ascii) :: Header] 
                , redirectCount = 0
                , checkStatus = \_ _ -> Nothing
                , requestBody =  RequestBodyLBS $ (renderLBS def queryDoc)
                }

        res <- http req2 manager
        return $ res

The following works and returns '200':

let pingdoc = Document (Prologue [] Nothing []) (Element "SYSTEM" [] []) []
Response status headers body <- doPost pingdoc
return (H.statusCode status)

However, when I try and parse the Response body using xml-conduit, I run into problems:

Response status headers body <- doPost xmldoc
let xmlRes' = parseLBS def body

The resulting compilation error is:

Couldn't match expected type `L.ByteString'
            with actual type `Source m0 ByteString'
In the second argument of `parseLBS', namely `body'
In the expression: parseLBS def body
In an equation for `xmlRes'': xmlRes' = parseLBS def body

I've tried connecting the Source from http-conduit to the xml-conduit using $= and $$, but I'm not having any success.

Does anyone have any hints to point me in the right direction? Thanks in advance.

Neil

1条回答
ゆ 、 Hurt°
2楼-- · 2019-05-07 12:21

You could use httpLbs rather than http, so that it returns a lazy ByteString rather than a Source — the parseLBS function is named because that's what it takes: a Lazy ByteString. However, it's probably best to use the conduit interface that the two are based on directly, as you mentioned. To do this, you should remove the runResourceT line from doPost, and use the following to get an XML document:

xmlRes' <- runResourceT $ do
    Response status headers body <- doPost xmldoc
    body $$ sinkDoc def

This uses xml-conduit's sinkDoc function, connecting the Source from http-conduit to the Sink from xml-conduit.

Once they're connected, the complete pipeline has to be run using runResourceT, which ensures all allocated resources are released in a timely fashion. The problem with your original code is that it runs the ResourceT too early, from inside doPost; you should generally use runResourceT right at the point that you want an actual result out, because a pipeline has to run entirely within the scope of a single ResourceT.

By the way, res <- http req2 manager; return $ res can be simplified to just http req2 manager.

查看更多
登录 后发表回答