packages feed

aeson-jsonpath 0.4.0.0 → 0.4.1.0

raw patch · 11 files changed

+251/−29 lines, 11 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Aeson.JSONPath: dumpQuery :: Query -> Text
+ Data.Aeson.JSONPath.Query: dumpQuery :: Query -> Text

Files

CHANGELOG.md view
@@ -2,6 +2,10 @@  All notable changes to this package are documented in this file. This project adheres to [Haskell PVP](https://pvp.haskell.org/) versioning. +## 0.4.1.0++- #85, Implement `dumpQuery` to dump JSONPath query+ ## 0.4.0.0  - #82, Implement `search()` function from function extensions
README.md view
@@ -1,6 +1,6 @@ # aeson-jsonpath -[![Build](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/build.yml/badge.svg)](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/build.yml) [![hackage-docs](https://img.shields.io/badge/hackage-v0.4.0.0-blue)](https://hackage.haskell.org/package/aeson-jsonpath) [![Donate](https://img.shields.io/badge/Donate-Patreon-red)](https://www.patreon.com/taimoorzaeem) [![Compliance](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/compliance.yml/badge.svg)](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/compliance.yml)+[![Build](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/build.yml/badge.svg)](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/build.yml) [![hackage-docs](https://img.shields.io/badge/hackage-v0.4.1.0-blue)](https://hackage.haskell.org/package/aeson-jsonpath) [![Compliance](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/compliance.yml/badge.svg)](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/compliance.yml)  Run [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535) compliant JSONPath queries on [Data.Aeson](https://hackage.haskell.org/package/aeson). 
aeson-jsonpath.cabal view
@@ -1,5 +1,6 @@+cabal-version:      3.0 name:               aeson-jsonpath-version:            0.4.0.0+version:            0.4.1.0 synopsis:           Parse and run JSONPath queries on Aeson documents description:        RFC 9535 compliant JSONPath parsing and querying                     package. JSONPath is similar to XPath for querying@@ -15,7 +16,6 @@ extra-source-files: CHANGELOG.md extra-doc-files:    README.md data-files:         jsonpath-compliance-test-suite/cts.json-cabal-version:      1.18  tested-with:     GHC == 9.4.8@@ -29,11 +29,16 @@   type: git   location: https://github.com/taimoorzaeem/aeson-jsonpath -library+common common-fields   default-language:   Haskell2010   default-extensions: OverloadedStrings                       NoImplicitPrelude                       QuasiQuotes++  ghc-options:        -Wall -Wunused-packages++library+  import:             common-fields   hs-source-dirs:     src   exposed-modules:    Data.Aeson.JSONPath                       Data.Aeson.JSONPath.Parser@@ -48,6 +53,7 @@                       Data.Aeson.JSONPath.Query.Segment                       Data.Aeson.JSONPath.Query.Selector                       Data.Aeson.JSONPath.Query.Filter+                      Data.Aeson.JSONPath.Query.DumpQuery                       Data.Aeson.JSONPath.Types.Query                       Data.Aeson.JSONPath.Types.Segment                       Data.Aeson.JSONPath.Types.Selector@@ -61,19 +67,15 @@                     , text                      >= 1.2.2 && < 2.2                     , vector                    >= 0.11 && < 0.14 -  ghc-options:        -Wall -Wunused-packages- test-suite spec+  import:             common-fields   type:               exitcode-stdio-1.0-  default-language:   Haskell2010-  default-extensions: OverloadedStrings-                      NoImplicitPrelude-                      QuasiQuotes   hs-source-dirs:     test/spec   main-is:            Main.hs   other-modules:      ParserSpec                       QuerySpec                       LocatedSpec+                      DumpQuerySpec   build-depends:      aeson                     >= 2.0.3 && < 2.5                     , aeson-jsonpath                     , base                      >= 4.9 && < 4.23@@ -81,18 +83,14 @@                     , parsec                    >= 3.1.11 && < 3.2                     , vector                    >= 0.11 && < 0.14 -  ghc-options:        -Wall -Wunused-packages- test-suite compliance+  import:             common-fields   type:               exitcode-stdio-1.0-  default-language:   Haskell2010-  default-extensions: OverloadedStrings-                      NoImplicitPrelude-                      QuasiQuotes   hs-source-dirs:     test/compliance   main-is:            Main.hs   other-modules:      ComplianceSpec                       Paths_aeson_jsonpath+  autogen-modules:    Paths_aeson_jsonpath   build-depends:      aeson                     >= 2.0.3 && < 2.5                     , aeson-jsonpath                     , base                      >= 4.9 && < 4.23@@ -100,5 +98,3 @@                     , parsec                    >= 3.1.11 && < 3.2                     , text                      >= 1.2.2 && < 2.2                     , vector                    >= 0.11 && < 0.14--  ghc-options:        -Wall -Wunused-packages
jsonpath-compliance-test-suite/cts.json view
@@ -8020,7 +8020,8 @@       ],       "tags": [         "function",-        "length"+        "length",+        "value"       ]     },     {@@ -8047,7 +8048,8 @@       ],       "tags": [         "function",-        "length"+        "length",+        "value"       ]     },     {@@ -8383,7 +8385,8 @@       ],       "tags": [         "function",-        "match"+        "match",+        "value"       ]     },     {@@ -8903,7 +8906,8 @@       ],       "tags": [         "function",-        "search"+        "search",+        "value"       ]     },     {@@ -9857,8 +9861,8 @@         "$[1]"       ],       "tags": [+        "count",         "function",-        "search",         "whitespace"       ]     },
src/Data/Aeson/JSONPath.hs view
@@ -20,6 +20,9 @@   , queryLocated   , queryLocatedQQ +  -- * Dump Query+  , dumpQuery+   -- * QuasiQuoter   , jsonPath   )@@ -33,7 +36,7 @@ import Language.Haskell.TH.Syntax  (lift)  import Data.Aeson.JSONPath.Parser  (pQuery)-import Data.Aeson.JSONPath.Query   (qQuery, qQueryLocated)+import Data.Aeson.JSONPath.Query   (qQuery, qQueryLocated, dumpQuery)  import Data.Aeson.JSONPath.Types 
src/Data/Aeson/JSONPath/Query.hs view
@@ -12,7 +12,9 @@ module Data.Aeson.JSONPath.Query   (     module Data.Aeson.JSONPath.Query.Query+  , module Data.Aeson.JSONPath.Query.DumpQuery   )   where  import Data.Aeson.JSONPath.Query.Query+import Data.Aeson.JSONPath.Query.DumpQuery
+ src/Data/Aeson/JSONPath/Query/DumpQuery.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE LambdaCase      #-}+{-# LANGUAGE RecordWildCards #-}+module Data.Aeson.JSONPath.Query.DumpQuery+  ( dumpQuery )+  where++import Data.Text                       (Text)++import qualified Data.Text             as T++import Data.Aeson.JSONPath.Types++import Prelude++-- | Dump JSONPath Query+dumpQuery :: Query -> Text+dumpQuery Query{..} = case queryType of+  Root    -> "$" <> T.concat (map dumpQuerySegment querySegments)+  Current -> "@" <> T.concat (map dumpQuerySegment querySegments)++-- |+dumpQuerySegment :: QuerySegment Query -> Text+dumpQuerySegment QuerySegment{..} = case segmentType of+  Child      -> dumpSegment True segment+  Descendant -> ".." <> dumpSegment False segment++-- |+dumpSegment :: Bool -> Segment Query -> Text+dumpSegment isChild = \case+  Bracketed selectors -> "[" <> T.intercalate "," (map dumpSelector selectors) <> "]"+  Dotted txt -> "." <> txt+  WildcardSegment -> if isChild then ".*" else "*"++-- |+dumpSelector :: Selector Query -> Text+dumpSelector = \case+  Name txt -> "'" <> txt <> "'"+  Index i -> T.pack (show i)+  ArraySlice (start, end, step) ->+    case (start, end) of+      (Just start', Just end') -> T.pack (show start' <> ":" <> show end' <> ":" <> show step)+      (Just start', Nothing)   -> T.pack (show start' <> "::" <> show step)+      (Nothing, Just end')     -> T.pack (":" <> show end' <> ":" <> show step)+      (Nothing, Nothing)       -> T.pack ("::"<> show step)+  Filter logicalOrExpr -> "?" <>  dumpLogicalOrExpr logicalOrExpr+  WildcardSelector -> "*"++-- |+dumpLogicalOrExpr :: LogicalOrExpr Query -> Text+dumpLogicalOrExpr (LogicalOr andExprs) = T.intercalate " || " (map dumpLogicalAndExpr andExprs)++-- |+dumpLogicalAndExpr :: LogicalAndExpr Query -> Text+dumpLogicalAndExpr (LogicalAnd basicExprs) = T.intercalate " || " (map dumpBasicExpr basicExprs)++-- |+dumpBasicExpr :: BasicExpr Query -> Text+dumpBasicExpr = \case+  Paren orExpr -> "(" <> dumpLogicalOrExpr orExpr <> ")"+  NotParen orExpr -> "!(" <> dumpLogicalOrExpr orExpr <> ")"+  Test testExpr -> dumpTestExpr testExpr+  NotTest testExpr -> "!" <> dumpTestExpr testExpr+  Comparison expr -> dumpComparisonExpr expr++-- |+dumpTestExpr :: TestExpr Query -> Text+dumpTestExpr = \case+  FilterQuery q -> dumpQuery q+  TestFunc funcExpr -> dumpFunctionExpr funcExpr++-- |+dumpComparisonExpr :: ComparisonExpr -> Text+dumpComparisonExpr (Comp compLeft op compRight) = dumpComparable compLeft <> " " <> dumpComparisonOp op <> " " <> dumpComparable compRight++-- |+dumpComparisonOp :: ComparisonOp -> Text+dumpComparisonOp = \case+  Less -> "<"+  LessOrEqual -> "<="+  Greater -> ">"+  GreaterOrEqual -> ">="+  Equal -> "=="+  NotEqual -> "!="++-- |+dumpComparable :: Comparable -> Text+dumpComparable = \case+  CompLit lit -> dumpLiteral lit+  CompSQ sq -> dumpSingularQ sq++-- |+dumpLiteral :: Literal -> Text+dumpLiteral = \case+  LitString txt -> "'" <> txt <> "'"+  LitNum s -> T.pack $ show s+  LitBool b -> T.toLower $ T.pack $ show b+  LitNull -> "null"++-- |+dumpSingularQ :: SingularQuery -> Text+dumpSingularQ SingularQuery{..} = case singularQueryType of+  RootSQ -> "$" <> T.concat (map dumpSingularQSegment singularQuerySegments)+  CurrentSQ -> "@" <> T.concat (map dumpSingularQSegment singularQuerySegments)++-- |+dumpSingularQSegment :: SingularQuerySegment -> Text+dumpSingularQSegment = \case+  NameSQSeg txt -> "." <> txt+  IndexSQSeg i -> "[" <> T.pack (show i) <> "]"++-- |+dumpFunctionExpr :: FunctionExpr Query -> Text+dumpFunctionExpr FunctionSearch{..} =+  "search(" <> dumpFunctionArg functionArg1 <> ", " <> dumpFunctionArg functionArg2 <> ")"++-- |+dumpFunctionArg :: FunctionArg Query -> Text+dumpFunctionArg = \case+  ArgLit lit -> dumpLiteral lit+  ArgQuery q -> dumpQuery q+  ArgLogicExpr e -> dumpLogicalOrExpr e+  ArgFuncExpr f -> dumpFunctionExpr f
src/Data/Aeson/JSONPath/Types/Filter.hs view
@@ -101,6 +101,7 @@  -- | data FunctionExpr a =+  -- TODO: Remove functionName, not needed because Type Constructor already  tells us that   FunctionSearch {     functionName :: FunctionName   , functionArg1 :: FunctionArg a
test/compliance/ComplianceSpec.hs view
@@ -63,21 +63,17 @@     mapM_ runTestCase tests  -- Skip 5 tests because these tests are not according to Haskell TDFA regular expression.--- Skip 2 tests that include other functions like "value()" or "count()" which are not yet--- implemented. TODO: These can be removed, if tagged accordingly searchFunctionTestsToBeSkipped :: [String] searchFunctionTestsToBeSkipped = [     "functions, search, filter, search function, unicode char class, uppercase"   , "functions, search, filter, search function, unicode char class negated, uppercase"   , "functions, search, dot matcher on \\u2028"   , "functions, search, dot matcher on \\u2029"-  , "functions, search, arg is a function expression"+  , "functions, search, arg is a function expression" -- skipped bcz it has "value()" function, which is not implemented yet   , "functions, search, escaped right square bracket"-  , "whitespace, functions, space between arg and parenthesis"   ]  runTestCase :: TestCase -> SpecWith ()--- skip function extension tests runTestCase tc@TestCase{tags=Just xs, ..} =   if ("function" `elem` xs && not ("search" `elem` xs)) || (name `elem` searchFunctionTestsToBeSkipped)     then xit name pending
+ test/spec/DumpQuerySpec.hs view
@@ -0,0 +1,92 @@+module DumpQuerySpec+  ( spec )+  where++import Test.Hspec++import Data.Aeson.JSONPath  (jsonPath, dumpQuery)++import Prelude++spec :: Spec+spec = do+  describe "test query dump" $ do+    it "should dump the function query" $+      dumpQuery [jsonPath|$.store.books[?search(@.author, 'Diamond|Herman')]|]+      `shouldBe` "$.store.books[?search(@.author, 'Diamond|Herman')]"++    it "should dump filter test query" $+      dumpQuery [jsonPath|$.store.books[?@.not_here]|]+      `shouldBe` "$.store.books[?@.not_here]"++    it "should dump filter comparison query" $+      dumpQuery [jsonPath|$.store.books[?@.price < 20]|]+      `shouldBe` "$.store.books[?@.price < 20.0]"++    it "should dump root" $+      dumpQuery [jsonPath|$|]+      `shouldBe` "$"++    it "should dump dotted segment" $+      dumpQuery [jsonPath|$.store|]+      `shouldBe` "$.store"++    it "should dump bracketed segment" $+      dumpQuery [jsonPath|$['store']|]+      `shouldBe` "$['store']"++    it "should dump dotted segments" $+      dumpQuery [jsonPath|$.store.books|]+      `shouldBe` "$.store.books"++    it "should dump index selector" $+      dumpQuery [jsonPath|$.store.books[0]|]+      `shouldBe` "$.store.books[0]"++    it "should dump -ve index" $+      dumpQuery [jsonPath|$.store.books[-4]|]+      `shouldBe` "$.store.books[-4]"++    it "should dump multiple singular query segment" $+      dumpQuery [jsonPath|$.store.books[0,2]|]+      `shouldBe` "$.store.books[0,2]"++    it "should dump slice" $+      dumpQuery [jsonPath|$.store.books[1:3]|]+      `shouldBe` "$.store.books[1:3:1]"++    it "should dump multiple bracketed" $+      dumpQuery [jsonPath|$.store.books[1:3,0,1]|]+      `shouldBe` "$.store.books[1:3:1,0,1]"++    it "should dump slice with no end" $+      dumpQuery [jsonPath|$[5:]|]+      `shouldBe` "$[5::1]"++    it "should dump slice with start, end and step" $+      dumpQuery [jsonPath|$[1:5:2]|]+      `shouldBe` "$[1:5:2]"++    it "should dump slice with step -2" $+      dumpQuery [jsonPath|$[5:1:-2]|]+      `shouldBe` "$[5:1:-2]"++    it "should dump slice with step -1" $+      dumpQuery [jsonPath|$[::-1]|]+      `shouldBe` "$[::-1]"++    it "should dump child wildcard" $+      dumpQuery [jsonPath|$.*|]+      `shouldBe` "$.*"++    it "should dump segment wildcard" $+      dumpQuery [jsonPath|$[*]|]+      `shouldBe` "$[*]"++    it "should dump descendant wildcard" $+      dumpQuery [jsonPath|$..*|]+      `shouldBe` "$..*"++    it "should dump descendant bracketed" $+      dumpQuery [jsonPath|$..[*]|]+      `shouldBe` "$..[*]"
test/spec/Main.hs view
@@ -7,6 +7,7 @@ import qualified ParserSpec import qualified QuerySpec import qualified LocatedSpec+import qualified DumpQuerySpec  import Test.Hspec.Runner import Prelude@@ -17,6 +18,7 @@     ParserSpec.spec     QuerySpec.spec     LocatedSpec.spec+    DumpQuerySpec.spec   main :: IO ()