如何使用管道创建 json 漂亮的打印机

How to create json pretty printer with conduit

我正在学习 conduit 并想创建一个处理器,它可以从标准输入中获取 json 输入,以某种方式处理它,然后输出到标准输出。已经选择漂亮的印刷作为示例。

现在我只想解码 json 并打印它。但似乎我在 Bytestring 类型不匹配方面遇到了一些问题。

import Control.Monad.Trans.Resource
   import Data.Aeson
   import Data.Conduit
   import qualified Data.Conduit.Combinators as CC
   import Data.Conduit (await, yield, (.|))
   import Data.ByteString.Lazy.Char8 as BC
   import qualified Data.Conduit.Binary as CB
   import Data.JsonStream.Parser hiding ((.|))
   import qualified Data.Text as T
   import System.IO (stdin, stdout)


   jsonParse :: Conduit BC.ByteString IO (T.Text, Value)
   jsonParse = doParse parseOutput
     where
       parseOutput :: ParseOutput (T.Text, Value)
       parseOutput = runParser (objectItems value)

       doParse :: ParseOutput (T.Text, Value) -> Conduit BC.ByteString IO (T.Text, Value)
       doParse out = case out of
           ParseYield value newOutput -> do
               yield value
               doParse newOutput
           ParseNeedData cont ->
               awaitForever $ \i -> doParse (cont i)
           ParseDone remaining -> return ()
           ParseFailed err -> error err

   main = runConduit $ CB.sourceHandle stdin .| jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout


   /tmp/some1/app/Main.hs:25:13: error:
       • Couldn't match type ‘Data.ByteString.Internal.ByteString’
                        with ‘ByteString’
         NB: ‘ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
             ‘Data.ByteString.Internal.ByteString’
               is defined in ‘Data.ByteString.Internal’
         Expected type: Conduit ByteString IO (T.Text, Value)
           Actual type: ConduitM
                          Data.ByteString.Internal.ByteString (T.Text, Value) IO ()
       • In the expression: awaitForever $ \ i -> doParse (cont i)
         In a case alternative:
             ParseNeedData cont -> awaitForever $ \ i -> doParse (cont i)
         In the expression:
           case out of
             ParseYield value newOutput
               -> do yield value
                     doParse newOutput
             ParseNeedData cont -> awaitForever $ \ i -> doParse (cont i)
             ParseDone remaining -> return ()
             ParseFailed err -> error err
      |
   25 | awaitForever $ \i -> doParse (cont i)
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   /tmp/some1/app/Main.hs:25:34: error:
       • Couldn't match type ‘ByteString’
                        with ‘Data.ByteString.Internal.ByteString’
         NB: ‘Data.ByteString.Internal.ByteString’
               is defined in ‘Data.ByteString.Internal’
             ‘ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
         Expected type: ConduitM
                          Data.ByteString.Internal.ByteString (T.Text, Value) IO ()
           Actual type: Conduit ByteString IO (T.Text, Value)
       • In the expression: doParse (cont i)
         In the second argument of ‘($)’, namely ‘\ i -> doParse (cont i)’
         In the expression: awaitForever $ \ i -> doParse (cont i)
      |
   25 | awaitForever $ \i -> doParse (cont i)
      | ^^^^^^^^^^^^^^^^

   /tmp/some1/app/Main.hs:29:46: error:
       • Couldn't match type ‘ByteString’
                        with ‘Data.ByteString.Internal.ByteString’
         NB: ‘Data.ByteString.Internal.ByteString’
               is defined in ‘Data.ByteString.Internal’
             ‘ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
         Expected type: ConduitM
                          Data.ByteString.Internal.ByteString Data.Void.Void IO ()
           Actual type: ConduitM ByteString Data.Void.Void IO ()
       • In the second argument of ‘(.|)’, namely
           ‘jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout’
         In the second argument of ‘($)’, namely
           ‘CB.sourceHandle stdin
              .| jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout’
         In the expression:
           runConduit
             $ CB.sourceHandle stdin
                 .| jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout
      |
   29 | main = runConduit $ CB.sourceHandle stdin .| jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   /tmp/some1/app/Main.hs:29:84: error:
       • Couldn't match type ‘Data.ByteString.Internal.ByteString’
                        with ‘ByteString’
         NB: ‘ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
             ‘Data.ByteString.Internal.ByteString’
               is defined in ‘Data.ByteString.Internal’
         Expected type: ConduitM ByteString Data.Void.Void IO ()
           Actual type: ConduitM
                          Data.ByteString.Internal.ByteString Data.Void.Void IO ()
       • In the second argument of ‘(.|)’, namely ‘CB.sinkHandle stdout’
         In the second argument of ‘(.|)’, namely
           ‘CC.map (encode . snd) .| CB.sinkHandle stdout’
         In the second argument of ‘(.|)’, namely
           ‘jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout’
      |
   29 | main = runConduit $ CB.sourceHandle stdin .| jsonParse .| CC.map (encode . snd) .| CB.sinkHandle stdout
      | ^^^^^^^^^^^^^^^^^^^^

bytestring 定义了严格类型(在 Data.ByteString 中)和惰性类型(在模块 Data.ByteString.Lazy 中)。修复您的代码可能就像导入 Data.ByteString.Char8 而不是惰性代码一样简单。如果库使用混合物,您可以使用 toStrict and fromStrict

进行转换