Advertisement
Guest User

Untitled

a guest
Jul 19th, 2019
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.52 KB | None | 0 0
  1. {-# LANGUAGE DeriveDataTypeable #-}
  2. {-# LANGUAGE CPP #-}
  3.  
  4. -- | A version of aeson that parses with key order preserved.
  5. --
  6. -- Copyright: (c) 2019 Hasura, Inc.
  7. -- (c) 2011-2016 Bryan O'Sullivan
  8. -- (c) 2011 MailRank, Inc.
  9.  
  10. module Data.Parser.Json
  11. ( Value(..)
  12. , Object
  13. , value
  14. , decode
  15. , eitherDecode
  16. ) where
  17.  
  18. import Control.Applicative
  19. import Data.Aeson.Parser (jstring)
  20. import Data.Attoparsec.ByteString (Parser)
  21. import qualified Data.Attoparsec.ByteString as A
  22. import qualified Data.Attoparsec.ByteString.Char8 as A8
  23. import Data.ByteString (ByteString)
  24. import Data.Data
  25. import Data.Functor
  26. import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
  27. import qualified Data.HashMap.Strict.InsOrd as OMap
  28. import Data.Scientific
  29. import Data.Text (Text)
  30. import Data.Vector (Vector)
  31. import qualified Data.Vector as V
  32. import GHC.Generics
  33. import Prelude
  34. import Prelude hiding (error, undefined)
  35.  
  36. --------------------------------------------------------------------------------
  37. -- Copied constants from aeson
  38.  
  39. #define BACKSLASH 92
  40. #define CLOSE_CURLY 125
  41. #define CLOSE_SQUARE 93
  42. #define COMMA 44
  43. #define DOUBLE_QUOTE 34
  44. #define OPEN_CURLY 123
  45. #define OPEN_SQUARE 91
  46. #define C_0 48
  47. #define C_9 57
  48. #define C_A 65
  49. #define C_F 70
  50. #define C_a 97
  51. #define C_f 102
  52. #define C_n 110
  53. #define C_t 116
  54.  
  55. --------------------------------------------------------------------------------
  56. -- Our altered type
  57.  
  58. -- | A JSON \"object\" (key\/value map). This is where this type
  59. -- differs to the 'aeson' package.
  60. newtype Object = Object_ { unObject_ :: InsOrdHashMap Text Value}
  61. deriving (Eq, Read, Show, Typeable, Data, Generic)
  62.  
  63. -- | A JSON \"array\" (sequence).
  64. type Array = Vector Value
  65.  
  66. -- | A JSON value represented as a Haskell value. Intentionally
  67. -- shadowing the 'Value' from the aeson package.
  68. data Value
  69. = Object !Object
  70. | Array !Array
  71. | String !Text
  72. | Number !Scientific
  73. | Bool !Bool
  74. | Null
  75. deriving (Eq, Read, Show, Typeable, Data, Generic)
  76.  
  77. --------------------------------------------------------------------------------
  78. -- Top-level entry points
  79.  
  80. eitherDecode :: ByteString -> Either String Value
  81. eitherDecode = A.parseOnly value
  82.  
  83. decode :: ByteString -> Maybe Value
  84. decode = either (const Nothing) Just . A.parseOnly value
  85.  
  86. --------------------------------------------------------------------------------
  87. -- Modified aeson parser
  88.  
  89. -- Copied from the aeson package.
  90. arrayValues :: Parser Array
  91. arrayValues = do
  92. skipSpace
  93. w <- A.peekWord8'
  94. if w == CLOSE_SQUARE
  95. then A.anyWord8 >> return V.empty
  96. else loop [] 1
  97. where
  98. loop acc !len = do
  99. v <- (value A.<?> "json list value") <* skipSpace
  100. ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_SQUARE) A.<?> "',' or ']'"
  101. if ch == COMMA
  102. then skipSpace >> loop (v:acc) (len+1)
  103. else return (V.reverse (V.fromListN len (v:acc)))
  104. {-# INLINE arrayValues #-}
  105.  
  106. -- Copied from aeson package.
  107. objectValues :: Parser (InsOrdHashMap Text Value)
  108. objectValues = do
  109. skipSpace
  110. w <- A.peekWord8'
  111. if w == CLOSE_CURLY
  112. then A.anyWord8 >> return OMap.empty
  113. else loop OMap.empty
  114. where
  115. -- Why use acc pattern here, you may ask? because 'H.fromList' use 'unsafeInsert'
  116. -- and it's much faster because it's doing in place update to the 'HashMap'!
  117. loop acc = do
  118. k <- (jstring A.<?> "object key") <* skipSpace <* (A8.char ':' A.<?> "':'")
  119. v <- (value A.<?> "object value") <* skipSpace
  120. ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_CURLY) A.<?> "',' or '}'"
  121. let acc' = OMap.insert k v acc
  122. if ch == COMMA
  123. then skipSpace >> loop acc'
  124. else pure acc'
  125. {-# INLINE objectValues #-}
  126.  
  127. -- Copied from aeson package.
  128. value :: Parser Value
  129. value = do
  130. skipSpace
  131. w <- A.peekWord8'
  132. case w of
  133. DOUBLE_QUOTE -> A.anyWord8 *> (String <$> jstring)
  134. OPEN_CURLY -> A.anyWord8 *> (Object . Object_ <$> objectValues)
  135. OPEN_SQUARE -> A.anyWord8 *> (Array <$> arrayValues)
  136. C_f -> A8.string "false" $> Bool False
  137. C_t -> A8.string "true" $> Bool True
  138. C_n -> A8.string "null" $> Null
  139. _ | w >= 48 && w <= 57 || w == 45
  140. -> Number <$> A8.scientific
  141. | otherwise -> fail "not a valid json value"
  142. {-# INLINE value #-}
  143.  
  144. -- Copied from aeson package.
  145. -- | The only valid whitespace in a JSON document is space, newline,
  146. -- carriage return, and tab.
  147. skipSpace :: Parser ()
  148. skipSpace = A.skipWhile $ \w -> w == 0x20 || w == 0x0a || w == 0x0d || w == 0x09
  149. {-# INLINE skipSpace #-}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement