Created
October 25, 2023 19:31
-
-
Save Profpatsch/85d42f6bc656f188faba3336ee54652c to your computer and use it in GitHub Desktop.
nest json parser into postgres parsers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Postgres.Decoder where | |
import Control.Applicative (Alternative) | |
import Data.Aeson qualified as Json | |
import Data.Aeson.BetterErrors qualified as Json | |
import Data.Error.Tree | |
import Data.Typeable (Typeable) | |
import Database.PostgreSQL.Simple.FromField qualified as PG | |
import Database.PostgreSQL.Simple.FromRow qualified as PG | |
import Json qualified | |
import Label | |
import PossehlAnalyticsPrelude | |
-- | A Decoder of postgres values. Allows embedding more complex parsers (like a 'Json.ParseT'). | |
newtype Decoder a = Decoder (PG.RowParser a) | |
deriving newtype (Functor, Applicative, Alternative, Monad) | |
-- | Parse fields out of a json value returned from the database. | |
-- | |
-- ATTN: The whole json record has to be transferred before it is parsed, | |
-- so if you only need a tiny bit of it, use `->` and `->>` in your SQL statement | |
-- and return only the fields you need from the query. | |
-- | |
-- In that case pay attention to NULL though: | |
-- | |
-- @ | |
-- SELECT '{"foo": {}}'::jsonb->>'foo' IS NULL | |
-- → TRUE | |
-- @ | |
-- | |
-- Also note: `->>` will coerce the json value to @text@, regardless of the content. | |
-- So the JSON object @{"foo": {}}"@ would be returned as the text: @"{\"foo\": {}}"@. | |
-- | |
-- ATTN: If you need to parse timestamps, make sure to use 'Json.asUtcTimeLenient', | |
-- because timestamp formatting will be different, depending on the postgres @timezone@ setting. | |
json :: (Typeable a) => Json.ParseT ErrorTree Identity a -> Decoder a | |
json parser = Decoder $ PG.fieldWith $ \field bytes -> do | |
val <- PG.fromField @Json.Value field bytes | |
case Json.parseValue parser val of | |
Left err -> | |
PG.returnError | |
PG.ConversionFailed | |
field | |
(err & Json.parseErrorTree "Cannot decode jsonb column" & prettyErrorTree & textToString) | |
Right a -> pure a |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment