Kevin Meredith
Kevin Meredith

Reputation: 41909

Parser for JSON String

I'm trying to write a parser for a JSON String.

A valid example, per my parser, would be: "\"foobar\"" or "\"foo\"bar\"".

Here's what I attempted, but it does not terminate:

parseEscapedQuotes :: Parser String
parseEscapedQuotes = Parser f
  where
    f ('"':xs) = Just ("\"", xs)
    f _        = Nothing

parseStringJValue :: Parser JValue
parseStringJValue = (\x -> S (concat x)) <$> 
         ((char '"') *> 
         (zeroOrMore (alt parseEscapedQuotes (oneOrMore (notChar '"')))) 
         <* (char '"'))

My reasoning is that, I can have a repetition of either escaped quotes "\"" or characters not equal to ".

But it's not working as I expected:

ghci> runParser parseStringJValue "\"foobar\""
Nothing

Upvotes: 0

Views: 157

Answers (1)

ErikR
ErikR

Reputation: 52039

I don't know what parser combinator library you are using, but here is a working example using Parsec. I'm using monadic style to make it clearer what's going on, but it is easily translated to applicative style.

import Text.Parsec
import Text.Parsec.String

jchar :: Parser Char
jchar = escaped <|> anyChar

escaped :: Parser Char
escaped = do
  char '\\'
  c <- oneOf ['"', '\\', 'r', 't' ] -- etc.
  return $ case c of
            'r' -> '\r'
            't' -> '\t'
            _   -> c

jstringLiteral :: Parser String
jstringLiteral = do
  char '"'
  cs <- manyTill jchar (char '"') 
  return cs

test1 = parse jstringLiteral "" "\"This is a test\""
test2 = parse jstringLiteral "" "\"This is an embedded quote: \\\" after quote\""
test3 = parse jstringLiteral "" "\"Embedded return: \\r\""

Note the extra level of backslashes needed to represent parser input as Haskell string literals. Reading the input from a file would make creating the parser input more convenient.

The definition of the manyTill combinator is:

manyTill p end      = scan
  where
    scan  = do{ end; return [] }
            <|>
            do{ x <- p; xs <- scan; return (x:xs) }

and this might help you figure out why your definitions aren't working.

Upvotes: 1

Related Questions