Skip to content

Commit

Permalink
Merge pull request #353 from phadej/issue-311
Browse files Browse the repository at this point in the history
Resolve #311
  • Loading branch information
bergmark committed Feb 4, 2016
2 parents cfe2e24 + a147757 commit 28f5f5c
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 2 deletions.
23 changes: 21 additions & 2 deletions Data/Aeson/Types/Internal.hs
Expand Up @@ -67,7 +67,7 @@ import Control.DeepSeq (NFData(..))
import Control.Monad (MonadPlus(..), ap)
import qualified Control.Monad.Fail as Fail
import Data.ByteString.Builder (Builder, char7, toLazyByteString)
import Data.Char (isLower, isUpper, toLower)
import Data.Char (isLower, isUpper, toLower, isAlpha, isAlphaNum)
import Data.Data (Data)
import Data.HashMap.Strict (HashMap)
import Data.Hashable (Hashable(..))
Expand Down Expand Up @@ -459,9 +459,28 @@ parseEither m v = runParser (m v) [] onError Right
formatError :: JSONPath -> String -> String
formatError path msg = "Error in " ++ (format "$" path) ++ ": " ++ msg
where
format :: String -> JSONPath -> String
format pfx [] = pfx
format pfx (Index idx:parts) = format (pfx ++ "[" ++ show idx ++ "]") parts
format pfx (Key key:parts) = format (pfx ++ "." ++ unpack key) parts
format pfx (Key key:parts) = format (pfx ++ formatKey key) parts

formatKey :: Text -> String
formatKey key
| isIdentifierKey strKey = "." ++ strKey
| otherwise = "['" ++ escapeKey strKey ++ "']"
where strKey = unpack key

isIdentifierKey :: String -> Bool
isIdentifierKey [] = False
isIdentifierKey (x:xs) = isAlpha x && all isAlphaNum xs

escapeKey :: String -> String
escapeKey = concatMap escapeChar

escapeChar :: Char -> String
escapeChar '\'' = "\\'"
escapeChar '\\' = "\\\\"
escapeChar c = [c]

-- | A key\/value pair for an 'Object'.
type Pair = (Text, Value)
Expand Down
11 changes: 11 additions & 0 deletions tests/UnitTests.hs
Expand Up @@ -7,6 +7,7 @@ module UnitTests (ioTests, tests) where
import Control.Monad (forM)
import Data.Aeson (decode, eitherDecode, encode, genericToJSON, genericToEncoding, FromJSON(..), withObject, (.:), (.:?), (.:!))
import Data.Aeson.Encode (encodeToTextBuilder)
import Data.Aeson.Internal (JSONPathElement(..), formatError)
import Data.Aeson.TH (deriveJSON)
import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions, omitNothingFields)
import Data.Char (toUpper)
Expand Down Expand Up @@ -49,6 +50,9 @@ tests = testGroup "unit" [
testCase "good" $ utcTimeGood
, testCase "bad" $ utcTimeBad
]
, testGroup "formatError" [
testCase "example 1" $ formatErrorExample
]
, testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark
, testGroup "To JSON representation" $ fmap (testCase "-") jsonEncoding
, testGroup "From JSON representation" $ fmap (testCase "-") jsonDecoding
Expand Down Expand Up @@ -167,6 +171,13 @@ utcTimeBad = do
let (dec :: Maybe UTCTime) = decode . LT.encodeUtf8 $ (LT.concat ["\"", s, "\""]) in
assertEqual "verify failure" Nothing dec

-- Non identifier keys should be escaped & enclosed in brackets
formatErrorExample :: Assertion
formatErrorExample =
let rhs = formatError [Index 0, Key "foo", Key "bar", Key "a.b.c", Key "", Key "'\\", Key "end"] "error msg"
lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
in assertEqual "formatError example" lhs rhs

------------------------------------------------------------------------------
-- Comparison (.:?) and (.:!)
------------------------------------------------------------------------------
Expand Down

0 comments on commit 28f5f5c

Please sign in to comment.