2019-03-25 16:45:35 +03:00
|
|
|
module Data.Parser.JSONPath
|
|
|
|
( parseJSONPath
|
|
|
|
, JSONPathElement(..)
|
|
|
|
, JSONPath
|
|
|
|
) where
|
|
|
|
|
2020-04-20 11:55:09 +03:00
|
|
|
import Data.Bifunctor
|
|
|
|
import qualified Data.Text as T
|
|
|
|
import Prelude
|
|
|
|
|
|
|
|
import Control.Applicative
|
2019-03-25 16:45:35 +03:00
|
|
|
import Data.Aeson.Internal (JSONPath, JSONPathElement (..))
|
|
|
|
import Data.Attoparsec.Text
|
|
|
|
|
2020-04-20 11:55:09 +03:00
|
|
|
parseJSONPath :: T.Text -> Either String JSONPath
|
2020-04-21 11:06:11 +03:00
|
|
|
parseJSONPath "$" = Right []
|
2020-04-20 11:55:09 +03:00
|
|
|
parseJSONPath txt = first (const invalidMessage) $
|
|
|
|
parseOnly (optional (char '$') *> many1' element <* endOfInput) txt
|
|
|
|
where
|
|
|
|
invalidMessage = T.unpack txt
|
|
|
|
++ ". Accept letters, digits, underscore (_) or hyphen (-) only"
|
|
|
|
++ ". Use single quotes enclosed in bracket (['...']) if there is any special character"
|
2019-03-25 16:45:35 +03:00
|
|
|
|
2020-04-20 11:55:09 +03:00
|
|
|
element :: Parser JSONPathElement
|
|
|
|
element = Key <$> (optional (char '.') *> name) -- field or .field
|
|
|
|
<|> bracketElement -- [42] or ['field']
|
2019-03-25 16:45:35 +03:00
|
|
|
|
2020-04-20 11:55:09 +03:00
|
|
|
name :: Parser T.Text
|
|
|
|
name = go <?> "property name" where
|
|
|
|
go = do
|
|
|
|
firstChar <- letter
|
|
|
|
<|> char '_'
|
|
|
|
<?> "first character of property name must be a letter or underscore"
|
|
|
|
otherChars <- many' (letter <|> digit <|> satisfy (inClass "-_"))
|
|
|
|
pure $ T.pack (firstChar:otherChars)
|
2019-03-25 16:45:35 +03:00
|
|
|
|
2020-04-20 11:55:09 +03:00
|
|
|
-- | Parses a JSON property key or index in square bracket format, e.g.
|
|
|
|
-- > [42]
|
|
|
|
-- > ["hello"]
|
|
|
|
-- > ['你好']
|
|
|
|
bracketElement :: Parser JSONPathElement
|
|
|
|
bracketElement = do
|
|
|
|
optional (char '.') *> char '['
|
|
|
|
result <- Index <$> decimal
|
|
|
|
<|> Key <$> quotedString '"'
|
|
|
|
<|> Key <$> quotedString '\''
|
|
|
|
char ']'
|
|
|
|
pure result
|
2020-04-16 09:45:21 +03:00
|
|
|
where
|
2020-04-20 11:55:09 +03:00
|
|
|
quotedString delimiter = do
|
|
|
|
char delimiter
|
|
|
|
result <- T.pack <$> many' (charOrEscape delimiter)
|
|
|
|
char delimiter
|
|
|
|
pure result
|
|
|
|
|
|
|
|
charOrEscape delimiter = (char '\\' *> anyChar) <|> notChar delimiter
|