module Text.HJson.Query ( -- * Types Jsons ,JFilter -- * Building ,emptyObj ,(-->) ,(<>) ,(<.>) ,merges ,mergesRec -- * Filtering ,isObj ,isArr ,isStr ,isStrBy ,isNum ,isNumBy ,isBool ,isNull ,isPrimitive ,getFromKeys ,getFromObj ,getFromArr ,getFromIndex ,getFromIndexes ,getChildern ,getFromKey -- * Filter Combinators ,(>>>) ,(<+>) ,orElse ,when ,guards ,deepObj ,deepArr ,deep ) where import qualified Data.Map as M (empty, singleton, unionWith, elems, union, lookup) import qualified Data.Maybe as Mb (catMaybes) import qualified Data.List as L (nub) import qualified Text.HJson.Pretty as PP (toString) import Text.HJson type Jsons = [Json] type JFilter = Json -> Jsons infixl 1 >>> infixr 2 <+> infixr 3 --> -- Building -- | create empty JSON object emptyObj :: Json emptyObj = JObject (M.empty) -- | create JSON object (-->) :: String -> Json -> Json key --> val = JObject (M.singleton key val) -- | merge two JSON Objects (<>) :: Json -> Json -> Json (JObject x ) <> (JObject y) = JObject $ M.union x y (<>) _ _ = emptyObj -- | recursive merge two JSON Objects (<.>) :: Json -> Json -> Json (JObject x) <.> (JObject y) = JObject $ M.unionWith (\m n -> m <.> n) x y (<.>) _ _ = emptyObj -- | merge list JSON Objects merges :: [Json] -> Json merges [] = emptyObj merges js = foldl1 (<>) js -- | recursive merge lists JSON Objects -- -- Example: -- -- > j1 = jParse "{\"Europa\": {\"Ukraine\": [\"Kiyv\", \"Gitomir\", \"Lviv\"]}}" -- > j2 = jParse "{\"Asia\": {\"Japan\": [\"Tokyo\"]}}" -- > j3 = jParse "{\"Europa\": {\"UnitedKingdom\": [\"London\", \"Glasgow\"]}}" -- > j4 = jParse "{\"Europa\": {\"Germany\": [\"Berlin\", \"Bonn\"]}}" -- > j5 = jParse "{\"Africa\": {}}" -- > j6 = jParse"{\"America\": {\"USA\": [], \"Canada\": [\"Toronto\"]}}" -- > j7 = jParse "{\"Australia\": [\"Melburn\", \"Adelaida\"]}" -- > merg = mergsRec [j1, j2, j3, j4, j5, j6, j7] -- > ex0 = putJson merg -- -- Result: -- -- >{ -- > "Africa": { -- > }, -- > "America": { -- > "Canada": ["Toronto"], -- > "USA": [] -- > }, -- > "Asia": { -- > "Japan": ["Tokyo"] -- > }, -- > "Australia": ["Melburn", "Adelaida"], -- > "Europa": { -- > "Germany": ["Berlin", "Bonn"], -- > "Ukraine": ["Kiyv", "Gitomir", "Lviv"], -- > "UnitedKingdom": ["London", "Glasgow"] -- > } -- >} mergesRec :: [Json] -> Json mergesRec js = foldl (<.>) emptyObj js -- Fitering -- | filter JSON objects isObj :: JFilter isObj (JObject o) = [JObject o] isObj _ = [] -- | filter JSON arrays isArr :: JFilter isArr (JArray a) = [JArray a] isArr _ = [] -- | filter JSON strings isStr :: JFilter isStr (JString s) = [JString s] isStr _ = [] -- | predicative filter JSON strings isStrBy :: (String -> Bool) -> JFilter isStrBy p (JString s) = if p s then [JString s] else [] isStrBy _ _ = [] -- | filter JSON numbers isNum :: JFilter isNum (JNumber n) = [JNumber n] isNum _ = [] -- | predicative filter JSON numbers isNumBy :: Fractional a => (a -> Bool) -> JFilter isNumBy p (JNumber n) = if p (fromRational n) then [JNumber n] else [] isNumBy _ _ = [] -- | filter JSON Bool isBool :: JFilter isBool (JBool p) = [JBool p] isBool _ = [] -- | filter JSON null isNull :: JFilter isNull JNull = [JNull] isNull _ = [] -- | filter primitive types isPrimitive :: JFilter isPrimitive (JString s) = [JString s] isPrimitive (JNumber n) = [JNumber n] isPrimitive (JBool p) = [JBool p] isPrimitive JNull = [JNull] isPrimitive _ = [] -- | get elements from object with key getFromKey :: String -> JFilter getFromKey k (JObject m) = Mb.catMaybes [(M.lookup k m)] getFromKey _ _ = [] -- | get elements from object with keys -- -- Example: -- -- > query1 = getFromKeys ["Europa", "America", "Africa"] -- > json1 = query1 merg -- > ex1 = putJsons json1 -- -- Result: -- -- > { -- > "Germany": ["Berlin", "Bonn"], -- > "Ukraine": ["Kiyv", "Gitomir", "Lviv"], -- > "UnitedKingdom": ["London", "Glasgow"] -- > } -- > { -- > "Canada": ["Toronto"], -- > "USA": [] -- > } -- > { -- > -- > } getFromKeys :: [String] -> JFilter getFromKeys ks (JObject m) = Mb.catMaybes $ map (\k -> (M.lookup k m)) (L.nub ks) getFromKeys _ _ = [] -- | get all elements from object getFromObj :: JFilter getFromObj (JObject o) = M.elems o getFromObj _ = [] -- | get all elements from array getFromArr :: JFilter getFromArr (JArray a) = a getFromArr _ = [] -- | get element from array with index getFromIndex :: Int -> JFilter getFromIndex i (JArray a) = if i < length a then [a !! i] else [] getFromIndex _ _ = [] -- | get elements from array with indexes getFromIndexes :: [Int] -> JFilter getFromIndexes is ja = concat [getFromIndex i ja | i <- is] -- | get all elements from object and array getChildern :: JFilter getChildern (JObject o) = M.elems o getChildern (JArray a) = a getChildern _ = [] -- | Example: -- -- > query2 = query1 >>> getFromObj -- > json2 = query2 merg -- > ex2 = putJsons json2 -- -- Result: -- -- > ["Berlin", "Bonn"] -- > ["Kiyv", "Gitomir", "Lviv"] -- > ["London", "Glasgow"] -- > ["Toronto"] -- > [] (>>>) :: JFilter -> JFilter -> JFilter (f >>> g) t = concat [g t' | t' <- f t] -- | concat result two filters (<+>) :: JFilter -> JFilter -> JFilter (f <+> g) t = f t ++ g t -- | orElse :: JFilter -> JFilter -> JFilter orElse f g t | null res1 = g t | otherwise = res1 where res1 = f t -- | when :: JFilter -> JFilter -> JFilter when f g t | null (g t) = [t] | otherwise = f t -- | guards :: JFilter -> JFilter -> JFilter guards g f t | null (g t) = [] | otherwise = f t -- | tree traversal filter for object deepObj :: JFilter -> JFilter deepObj f = f `orElse` (getFromObj >>> deepObj f) -- | tree traversal filter for array deepArr :: JFilter -> JFilter deepArr f = f `orElse` (getFromArr >>> deepArr f) -- | tree traversal filter for objects and arrays -- -- Example: -- -- > -- Qwery: All city Europa, America, Australia and Africa -- > -- q31, q32, q33 is equal -- > -- > q31 = getFromKeys ["Europa", "America", "Africa", "Australia"] -- > >>> (getFromArr `orElse` getFromObj) -- > >>> (isStr `orElse` getFromArr) -- > -- > q32 = getFromKeys ["Europa", "America", "Africa", "Australia"] -- > >>> (getFromObj `when` isObj) -- > >>> getFromArr -- > -- > q33 = getFromKeys ["Europa", "America", "Africa", "Australia"] -- > >>> -- > deep getFromArr -- > -- -- See also: deep :: JFilter -> JFilter deep f = f `orElse` (getChildern >>> deep f)