Encode a value as a JSON ByteString via its type's codec.
Moduleautodocodec-0.5.0.0Haskell2010
Autodocodec
- 7 types
- 2 classes
- 97 values
- Packageautodocodec-0.5.0.0
- Exports106
- LanguageHaskell2010
- LicenceMIT
- SourceAeson.hs
Encoding and decoding JSON
2 declarationsParse a JSON ByteString using a type's codec.
Instantiating Aeson.ToJSON
4 declarationsImplement toJSON via a type's codec.
Implement toJSON via a given codec.
Implement toEncoding via a type's codec.
Implement toEncoding via the given codec.
JSON Objects
Instantiating Aeson.FromJSON
2 declarationsImplement parseJSON via a type's codec.
Implement parseJSON via a given codec.
JSON Objects
Codec
4 declarationsA completed autodocodec for parsing and rendering a Value.
You can use a value of this type to get everything else for free:
Encode values to JSON using
toJSONViaCodecortoJSONViaDecode values from JSON using
parseJSONViaCodecorparseJSONViaProduce a JSON Schema using
jsonSchemaViaCodecorjsonSchemaViafromautodocodec-schemaEncode to and decode from Yaml using
autodocodec-yamlProduce a human-readible YAML schema using
renderColouredSchemaViaCodecfromautodocodec-yamlProduce a Swagger2 schema using
autodocodec-swagger2Produce a OpenAPI3 schema using
autodocodec-openapi3
A completed autodocodec for parsing and rendering a Object.
A class for values which have a canonical codec.
There are no formal laws for this class. If you really want a law, it should be "Whomever uses the codec from your instance should not be surprised."
Methods
codec :: JSONCodec valueA codec for a single value
See the sections on helper functions for implementing this for plenty of examples.
listCodecForStringCompatibility :: JSONCodec [value]
Instances45HasCodec, …
HasCodec ValueDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec IntegerDefined in autodocodec-0.5.0.0 · Autodocodec.ClassThis instance uses the "safe" integerCodec.
HasCodec NaturalDefined in autodocodec-0.5.0.0 · Autodocodec.ClassThis instance uses the "safe" naturalCodec.
HasCodec VoidDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Int16Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Int32Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Int64Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Int8Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Word16Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Word32Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Word64Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec Word8Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec BoolDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec CharDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec IntDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec OrderingDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec WordDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec ScientificDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec TextDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec TextDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec DayDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec DiffTimeDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec NominalDiffTimeDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec UTCTimeDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec LocalTimeDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec TimeOfDayDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec ZonedTimeDefined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (First a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Last a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (DNonEmpty a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (DList a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (NonEmpty a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Identity a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (First a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Last a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Dual a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Maybe a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Vector a)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec [a]Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec v => HasCodec (KeyMap v)Defined in autodocodec-0.5.0.0 · Autodocodec.Class(Ord a, HasCodec a) => HasCodec (Set a)Defined in autodocodec-0.5.0.0 · Autodocodec.Class(HasCodec l, HasCodec r) => HasCodec (Either l r)Defined in autodocodec-0.5.0.0 · Autodocodec.Class(Eq k, Hashable k, FromJSONKey k, ToJSONKey k, HasCodec v) => HasCodec (HashMap k v)Defined in autodocodec-0.5.0.0 · Autodocodec.Class(Ord k, FromJSONKey k, ToJSONKey k, HasCodec v) => HasCodec (Map k v)Defined in autodocodec-0.5.0.0 · Autodocodec.ClassHasCodec a => HasCodec (Const a b)Defined in autodocodec-0.5.0.0 · Autodocodec.Class
A class for values which have a canonical object codec.
There are no formal laws for this class. If you really want a law, it should be "Whomever uses the codec from your instance should not be surprised."
Methods
objectCodec :: JSONObjectCodec objectA object codec for the value
See the sections on helper functions for implementing this for plenty of examples.
Writing a codec
3 declarationsAn object codec with a given name
Example usage
data Example = Example
{ exampleText :: !Text,
exampleBool :: !Bool
}
instance HasCodec Example where
codec =
object "Example" $
Example
<$> requiredField "text" "a text" .= exampleText
<*> requiredField "bool" "a bool" .= exampleBoolAPI Note
This is a forward-compatible version ObjectOfCodec with a name.
object name = ObjectOfCodec (Just name)Name a codec.
This is used to allow for references to the codec, and that's necessary to produce finite documentation for recursive codecs.
API Note
This is a forward-compatible version of ReferenceCodec.
named = ReferenceCodecProduce a codec using a type's FromJSON and ToJSON instances.
You will only want to use this if you cannot figure out how to produce a JSONCodec for your type.
Note that this will not have good documentation because, at a codec level, it's just parsing and rendering a Value.
Example usage
toJSONVia (codecViaAeson "Int") (5 :: Int)Number 5.0JSON.parseMaybe (parseJSONVia (codecViaAeson "Int")) (Number 5) :: Maybe IntJust 5
Field codecs
With documentation
A required field
During decoding, the field must be in the object.
During encoding, the field will always be in the object.
optionalField Infix version of lmapCodec
Use this function to supply the rendering side of a codec.
(.=) = flip lmapCodecExample usage
data Example = Example
{ exampleText :: !Text,
exampleBool :: !Bool
}
instance HasCodec Example where
codec =
object "Example" $
Example
<$> requiredField "text" .= exampleText
<*> requiredField "bool" .= exampleBooloptionalFieldOrNull optionalFieldWithDefault :: HasCodec output=> TextKey
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
An optional field with a default value
During decoding, the field may be in the object. The default value will be parsed otherwise.
During encoding, the field will be in the object. The default value is ignored.
The shown version of the default value will appear in the documentation.
requiredFieldWith :: TextKey
-> ValueCodec input outputCodec for the value
-> TextDocumentation
-> ObjectCodec input output
A required field
During decoding, the field must be in the object.
During encoding, the field will always be in the object.
optionalFieldWith :: TextKey
-> ValueCodec input outputCodec for the value
-> TextDocumentation
-> ObjectCodec (Maybe input) (Maybe output)
optionalFieldOrNullWith :: TextKey
-> ValueCodec input outputCodec for the value
-> TextDocumentation
-> ObjectCodec (Maybe input) (Maybe output)
optionalFieldWithDefaultWith :: TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
An optional field with default value
During decoding, the field may be in the object. The default value will be parsed otherwise.
During encoding, the field will always be in the object. The default value is ignored.
The shown version of the default value will appear in the documentation.
optionalFieldOrNullWithDefault :: (Eq output, HasCodec output)=> TextKey
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
Like optionalFieldOrNull, but also interpret null as the default value.
optionalFieldOrNullWithDefaultWith :: Eq output=> TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
Like optionalFieldWithDefaultWith, but also interpret null as the
default value.
optionalFieldWithOmittedDefault :: (Eq output, HasCodec output)=> TextKey
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
optionalFieldWithOmittedDefaultWith :: Eq output=> TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
An optional field with default value that can be omitted when encoding
During decoding, the field may be in the object. The default value will be parsed otherwise.
During encoding, the field will be omitted from the object if it is equal to the default value.
The shown version of the default value will appear in the documentation.
optionalFieldOrNullWithOmittedDefault :: (Eq output, HasCodec output)=> TextKey
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
optionalFieldOrNullWithOmittedDefaultWith :: Eq output=> TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> TextDocumentation
-> ObjectCodec output output
Like optionalFieldWithOmittedDefaultWith, but the value may also be
null and that will be interpreted as the default value.
Documentation-less versions of field codecs
Like requiredField, but without documentation
Like optionalField, but without documentation
Like optionalFieldOrNull, but without documentation
optionalFieldWithDefault' :: HasCodec output=> TextKey
-> outputDefault value
-> ObjectCodec output output
Like optionalFieldWithDefault, but without documentation
requiredFieldWith' :: TextKey
-> ValueCodec input outputCodec for the value
-> ObjectCodec input output
Like requiredFieldWith, but without documentation.
optionalFieldWith' :: TextKey
-> ValueCodec input outputCodec for the value
-> ObjectCodec (Maybe input) (Maybe output)
Like optionalFieldWith, but without documentation.
optionalFieldOrNullWith' :: TextKey
-> ValueCodec input outputCodec for the value
-> ObjectCodec (Maybe input) (Maybe output)
Like optionalFieldOrNullWith, but without documentation
optionalFieldWithDefaultWith' :: TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> ObjectCodec output output
Like optionalFieldWithDefaultWith, but without documentation.
optionalFieldOrNullWithDefault' :: (Eq output, HasCodec output)=> TextKey
-> outputDefault value
-> ObjectCodec output output
Like optionalFieldOrNullWithDefault, but without documentation
optionalFieldOrNullWithDefaultWith' :: Eq output=> TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> ObjectCodec output output
Like optionalFieldOrNullWithDefaultWith, but without documentation.
optionalFieldWithOmittedDefault' :: (Eq output, HasCodec output)=> TextKey
-> outputDefault value
-> ObjectCodec output output
optionalFieldWithOmittedDefaultWith' :: Eq output=> TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> ObjectCodec output output
Like optionalFieldWithOmittedDefaultWith, but without documentation.
optionalFieldOrNullWithOmittedDefault' :: (Eq output, HasCodec output)=> TextKey
-> outputDefault value
-> ObjectCodec output output
optionalFieldOrNullWithOmittedDefaultWith' :: Eq output=> TextKey
-> JSONCodec outputCodec for the value
-> outputDefault value
-> ObjectCodec output output
Like optionalFieldWithOmittedDefaultWith', but the value may also be
null and that will be interpreted as the default value.
Writing your own value codecs.
Primitive codecs
Codec for null
Example usage
toJSONVia nullCodec ()NullJSON.parseMaybe (parseJSONVia nullCodec) NullJust ()JSON.parseMaybe (parseJSONVia nullCodec) (Number 5)Nothing
API Note
This is a forward-compatible version of NullCodec.
nullCodec = NullCodecCodec for boolean values
Example usage
toJSONVia boolCodec TrueBool True
API Note
This is a forward-compatible version of BoolCodec without a name.
boolCodec = BoolCodec NothingCodec for text values
Example usage
toJSONVia textCodec "hello"String "hello"
API Note
This is a forward-compatible version of StringCodec without a name.
textCodec = StringCodec NothingCodec for Integer values
This codec does a bounds check for the range [-10^1024, 10^1024] it can safely parse very large numbers.
Example usage
toJSONVia integerCodec 5Number 5.0toJSONVia integerCodec (-1000000000000)Number (-1.0e12)JSON.parseMaybe (parseJSONVia integerCodec) (Number (-4.0))Just (-4)JSON.parseMaybe (parseJSONVia integerCodec) (Number (scientific 1 100000000))Nothing
API Note
This is a forward-compatible version of IntegerCodec without a name.
scientificCodec = IntegerCodec Nothing NothingFor a codec without this protection, see unsafeUnboundedIntegerCodec.
Codec for Integer values with bounds
Example usage
let c = integerWithBoundsCodec Bounds {boundsLower = Just 2, boundsUpper = Just 4}toJSONVia c 3Number 3.0toJSONVia c 5Number 5.0JSON.parseMaybe (parseJSONVia c) (Number 3)Just 3JSON.parseMaybe (parseJSONVia c) (Number 5)Nothing
API Note
This is a forward-compatible version of IntegerCodec without a name.
integerWithBoundsCodec bounds = IntegerCodec Nothing boundsCodec for Scientific values
Example usage
toJSONVia scientificCodec 5Number 5.0JSON.parseMaybe (parseJSONVia scientificCodec) (Number 3)Just 3.0
WARNING
Scientific is a type that is only for JSON parsing and rendering. Do not use it for any calculations. Instead, convert to another number type before doing any calculations.
λ> (1 / 3) :: Scientific
*** Exception: fromRational has been applied to a repeating decimal which can't be represented as a Scientific! It's better to avoid performing fractional operations on Scientifics and convert them to other fractional types like Double as early as possible.
API Note
This is a forward-compatible version of NumberCodec without a name.
scientificCodec = NumberCodec Nothing NothingCodec for Scientific values with bounds
Example usage
let c = scientificWithBoundsCodec Bounds {boundsLower = Just 2, boundsUpper = Just 4}toJSONVia c 3Number 3.0toJSONVia c 5Number 5.0JSON.parseMaybe (parseJSONVia c) (Number 3)Just 3.0JSON.parseMaybe (parseJSONVia c) (Number 5)Nothing
WARNING
Scientific is a type that is only for JSON parsing and rendering. Do not use it for any calculations. Instead, convert to another number type before doing any calculations.
λ> (1 / 3) :: Scientific
*** Exception: fromRational has been applied to a repeating decimal which can't be represented as a Scientific! It's better to avoid performing fractional operations on Scientifics and convert them to other fractional types like Double as early as possible.
API Note
This is a forward-compatible version of NumberCodec without a name.
scientificWithBoundsCodec bounds = NumberCodec Nothing boundsCodec for a Value
This is essentially your escape-hatch for when you would normally need a monad instance for Codec. You can build monad parsing by using valueCodec together with bimapCodec and supplying your own parsing function.
Note that this _does_ mean that the documentation will just say that you are parsing and rendering a value, so you may want to document the extra parsing further using <?>.
API Note
This is a forward-compatible version of ValueCodec.
valueCodec = ValueCodecBounds
Constructors
BoundsboundsLower :: !Maybe aLower bound, inclusive
boundsUpper :: !Maybe a
Instances7Functor, Eq, Ord, Show, Generic, Validity, …
Functor BoundsDefined in autodocodec-0.5.0.0 · Autodocodec.CodecEq a => Eq (Bounds a)Defined in autodocodec-0.5.0.0 · Autodocodec.CodecOrd a => Ord (Bounds a)Defined in autodocodec-0.5.0.0 · Autodocodec.CodecShow a => Show (Bounds a)Defined in autodocodec-0.5.0.0 · Autodocodec.CodecGeneric (Bounds a)Defined in autodocodec-0.5.0.0 · Autodocodec.CodecValidity a => Validity (Bounds a)Defined in autodocodec-0.5.0.0 · Autodocodec.Codectype Rep (Bounds a) = D1 ('MetaDataDefined in autodocodec-0.5.0.0 · Autodocodec.Codec"Bounds"
"Autodocodec.Codec"
"autodocodec-0.5.0.0-I0CkiyHlC5r32SQbfvaiPf"
'False) (C1 ('MetaCons"Bounds"
'PrefixI 'True) (S1 ('MetaSel ('Just"boundsLower"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe a)) :*: S1 ('MetaSel ('Just"boundsUpper"
) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe a))))
Check if a number falls within given NumberBounds.
Integral codecs
A codec for bounded integers like Int, Int8, and Word.
This codec will not have a name, and it will use the boundedNumberBounds to add number bounds.
let c = boundedIntegralCodec :: JSONCodec Int8toJSONVia c 5Number 5.0JSON.parseMaybe (parseJSONVia c) (Number 100)Just 100JSON.parseMaybe (parseJSONVia c) (Number 200)Nothing
This is an unsafe (unchecked) version of integerCodec.
A codec for Natural values.
This codec does a bounds check for the range [0, 10^1024] it can safely parse very large numbers.
For a codec without this protection, see unsafeUnboundedNaturalCodec.
Example usage
toJSONVia naturalCodec 5Number 5.0toJSONVia naturalCodec (1000000000000)Number 1.0e12JSON.parseMaybe (parseJSONVia naturalCodec) (Number 4.0)Just 4JSON.parseMaybe (parseJSONVia naturalCodec) (Number (scientific 1 100000000))Nothing
This is an unsafe (unchecked) version of naturalCodec.
Literal value codecs
A codec for a literal piece of Text.
During parsing, only the given Text is accepted.
During rendering, the given Text is always output.
Example usage
let c = literalTextCodec "hello"toJSONVia c "hello"String "hello"toJSONVia c "world"String "hello"JSON.parseMaybe (parseJSONVia c) (String "hello")Just "hello"JSON.parseMaybe (parseJSONVia c) (String "world")Nothing
A codec for a literal value corresponding to a literal piece of Text.
During parsing, only the given Text is accepted.
During rendering, the given value is always output.
Example usage
let c = literalTextValueCodec True "yes"toJSONVia c TrueString "yes"toJSONVia c FalseString "yes"JSON.parseMaybe (parseJSONVia c) (String "yes") :: Maybe BoolJust TrueJSON.parseMaybe (parseJSONVia c) (String "no") :: Maybe BoolNothing
Enums
A codec for a Bounded Enum that uses its Show instance to have the values correspond to literal Text values.
Example usage
data Fruit = Apple | Orange deriving (Show, Eq, Enum, Bounded)let c = shownBoundedEnumCodectoJSONVia c AppleString "Apple"JSON.parseMaybe (parseJSONVia c) (String "Orange") :: Maybe FruitJust Orange
A codec for a Bounded Enum that uses the provided function to have the values correspond to literal Text values.
Example usage
data Fruit = Apple | Orange deriving (Show, Eq, Enum, Bounded):{ let c = boundedEnumCodec $ \case Apple -> "foo" Orange -> "bar":}
toJSONVia c AppleString "foo"JSON.parseMaybe (parseJSONVia c) (String "bar") :: Maybe FruitJust Orange
A codec for an enum that can be written as constant string values
Example usage
data Fruit = Apple | Orange deriving (Show, Eq)let c = stringConstCodec [(Apple, "foo"), (Orange, "bar")]toJSONVia c OrangeString "bar"JSON.parseMaybe (parseJSONVia c) (String "foo") :: Maybe FruitJust Apple
WARNING
If you don't provide a string for one of the type's constructors, the last string in the list will be used instead:
let c = stringConstCodec [(Apple, "foo")]toJSONVia c OrangeString "foo"
A codec for an enum that can be written each with their own codec.
WARNING
If you don't provide a string for one of the type's constructors, the last codec in the list will be used instead.
Sum type codecs
Either codec
During encoding, parse a value according to either codec. During encoding, use the corresponding codec to encode either value.
HasCodec instance for sum types
To write a HasCodec instance for sum types, you will need to decide whether encoding is disjoint or not.
The default, so also the implementation of this function, is possiblyJointEitherCodec, but you may want to use disjointEitherCodec instead.
Ask yourself: Can the encoding of a Left value be decoded as Right value (or vice versa)?
Yes -> use possiblyJointEitherCodec.
No -> use disjointEitherCodec.
Example usage
let c = eitherCodec codec codec :: JSONCodec (Either Int String)toJSONVia c (Left 5)Number 5.0toJSONVia c (Right "hello")String "hello"JSON.parseMaybe (parseJSONVia c) (String "world") :: Maybe (Either Int String)Just (Right "world")
API Note
This is a forward-compatible version of possiblyJointEitherCodec.
eitherCodec = possiblyJointEitherCodecPossibly joint either codec
During encoding, parse a value according to either codec. During encoding, use the corresponding codec to encode either value.
This codec is for the case in which parsing must be disjoint.
HasCodec instance for sum types with an encoding that is definitely disjoint.
The eitherCodec can be used to implement HasCodec instances for sum types
for which the encoding is definitely disjoint.
data War = WorldWar Word8 | OtherWar Text deriving (Show, Eq):{ instance HasCodec War where codec = dimapCodec f g $ disjointEitherCodec (codec :: JSONCodec Word8) (codec :: JSONCodec Text) where f = \case Left w -> WorldWar w Right t -> OtherWar t g = \case WorldWar w -> Left w OtherWar t -> Right t:}
Note that this incoding is indeed disjoint because an encoded String can never be parsed as an Word8 and vice versa.
toJSONViaCodec (WorldWar 2)Number 2.0toJSONViaCodec (OtherWar "OnDrugs")String "OnDrugs"JSON.parseMaybe parseJSONViaCodec (String "of the roses") :: Maybe WarJust (OtherWar "of the roses")
WARNING
If it turns out that the encoding of a value is not disjoint, decoding may fail and documentation may be wrong.
let c = disjointEitherCodec (codec :: JSONCodec Int) (codec :: JSONCodec Int)JSON.parseMaybe (parseJSONVia c) (Number 5) :: Maybe (Either Int Int)Nothing
Encoding still works as expected, however:
toJSONVia c (Left 5)Number 5.0toJSONVia c (Right 6)Number 6.0
Example usage
toJSONVia (disjointEitherCodec (codec :: JSONCodec Int) (codec :: JSONCodec String)) (Left 5)Number 5.0toJSONVia (disjointEitherCodec (codec :: JSONCodec Int) (codec :: JSONCodec String)) (Right "hello")String "hello"
API Note
This is a forward-compatible version of 'EitherCodec DisjointUnion'.
disjointEitherCodec = EitherCodec DisjointUnionPossibly joint either codec
During encoding, parse a value according to either codec. During encoding, use the corresponding codec to encode either value.
This codec is for the case in which parsing may not be disjoint.
HasCodec instance for sum types with an encoding that is not disjoint.
The eitherCodec can be used to implement HasCodec instances for sum types.
If you just have two codecs that you want to try in order, while parsing, you can do this:
:{ data Ainur = Valar Text Text | Maiar Text deriving (Show, Eq):}
:{ instance HasCodec Ainur where codec = dimapCodec f g $ possiblyJointEitherCodec (object "Valar" $ (,) <$> requiredField "domain" "Domain which the Valar rules over" .= fst <*> requiredField "name" "Name of the Valar" .= snd) (object "Maiar" $ requiredField "name" "Name of the Maiar") where f = \case Left (domain, name) -> Valar domain name Right name -> Maiar name g = \case Valar domain name -> Left (domain, name) Maiar name -> Right name:}
Note that this encoding is indeed not disjoint, because a Valar object can
parse as a Maiar value.
toJSONViaCodec (Valar "Stars" "Varda")Object (fromList [("domain",String "Stars"),("name",String "Varda")])toJSONViaCodec (Maiar "Sauron")Object (fromList [("name",String "Sauron")])JSON.parseMaybe parseJSONViaCodec (Object (Compat.fromList [("name",String "Olorin")])) :: Maybe AinurJust (Maiar "Olorin")
WARNING
The order of the codecs in a possiblyJointEitherCodec matters.
In the above example, decoding works as expected because the Valar case is parsed first.
If the Maiar case were first in the possiblyJointEitherCodec, then
Valar could never be parsed.
API Note
This is a forward-compatible version of 'EitherCodec PossiblyJointUnion'.
possiblyJointEitherCodec = EitherCodec PossiblyJointUnionDiscriminated unions
Wrap up a value of type b with its codec to produce
and encoder for as that ignores its input and instead encodes
the value b.
This is useful for building discriminatedUnionCodecs.
Map a codec for decoding bs into a decoder for as.
This is useful for building discriminatedUnionCodecs.
discriminatedUnionCodec :: TextpropertyName
-> (input -> (Discriminator, ObjectCodec input ()))how to encode the input
Use mapToEncoder to produce the ObjectCodecs.
-> HashMap Discriminator (Text, ObjectCodec Void output)how to decode the output
The Text field is the name to use for the object schema.
Use mapToDecoder to produce the ObjectCodecs.
-> ObjectCodec input output
Encode/decode a discriminated union of objects
The type of object being encoded/decoded is discriminated by a designated "discriminator" property on the object which takes a string value.
When encoding, the provided function is applied to the input to obtain a new encoder
for the input. The function mapToEncoder is provided to assist with building these
encoders. See examples in Usage.hs.
When decoding, the value of the discriminator property is looked up in the HashMap
to obtain a decoder for the output. The function mapToDecoder is provided
to assist with building these decoders. See examples in Usage.hs.
The HashMap is also used to generate schemas for the type.
In particular, for OpenAPI 3, it will generate a schema with a discriminator, as defined
by https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/
API Note
This is a forward-compatible version of DiscriminatedUnionCodec.
discriminatedUnionCodec = 'DiscriminatedUnionCodec'Mapping
dimapCodec Map both directions of a codec
You can use this function to change the type of a codec as long as the two functions are inverses.
HasCodec instance for newtypes
A good use-case is implementing HasCodec for newtypes:
newtype MyInt = MyInt { unMyInt :: Int }
instance HasCodec MyInt where
codec = dimapCodec MyInt unMyInt codecMap a codec's input and output types.
This function allows you to have the parsing fail in a new way.
If you use this function, then you will most likely want to add documentation about how not every value that the schema specifies will be accepted.
This function is like BimapCodec except it also combines one level of a nested BimapCodecs.
Example usage
logLevelCodec :: JSONCodec LogLevel logLevelCodec = bimapCodec parseLogLevel renderLogLevel codec ? "Valid values include DEBUG, INFO, WARNING, ERROR."
Map the output part of a codec
You can use this function if you only need to map the parsing-side of a codec. This function is probably only useful if the function you map does not change the codec type.
WARNING: This can be used to produce a codec that does not roundtrip.
JSON.parseMaybe (parseJSONVia (rmapCodec (*2) codec)) (Number 5) :: Maybe IntJust 10
Map the input part of a codec
You can use this function if you only need to map the rendering-side of a codec. This function is probably only useful if the function you map does not change the codec type.
WARNING: This can be used to produce a codec that does not roundtrip.
toJSONVia (lmapCodec (*2) (codec :: JSONCodec Int)) 5Number 10.0
Composing codecs
Maybe codec
This can be used to also allow null during decoding of a Maybe value.
During decoding, also accept a null value as Nothing.
During encoding, encode as usual.
Example usage
toJSONVia (maybeCodec codec) (Just 'a')String "a"toJSONVia (maybeCodec codec) (Nothing :: Maybe Char)Null
List codec
Build a codec for lists of values from a codec for a single value.
Example usage
toJSONVia (listCodec codec) ['a','b']Array [String "a",String "b"]
API Note
This is the list version of vectorCodec.
Build a codec for nonempty lists of values from a codec for a single value.
Example usage
toJSONVia (nonEmptyCodec codec) ('a' :| ['b'])Array [String "a",String "b"]
API Note
This is the non-empty list version of vectorCodec.
Single or list codec
This codec behaves like listCodec, except the values may also be simplified as a single value.
During parsing, a single element may be parsed as the list of just that element. During rendering, a list with only one element will be rendered as just that element.
Example usage
let c = singleOrListCodec codec :: JSONCodec [Int]toJSONVia c [5]Number 5.0toJSONVia c [5,6]Array [Number 5.0,Number 6.0]JSON.parseMaybe (parseJSONVia c) (Number 5) :: Maybe [Int]Just [5]JSON.parseMaybe (parseJSONVia c) (Array [Number 5, Number 6]) :: Maybe [Int]Just [5,6]
WARNING
If you use nested lists, for example when the given value codec is also a listCodec, you may get in trouble with ambiguities during parsing.
Single or nonempty list codec
This codec behaves like nonEmptyCodec, except the values may also be simplified as a single value.
During parsing, a single element may be parsed as the list of just that element. During rendering, a list with only one element will be rendered as just that element.
Example usage
let c = singleOrNonEmptyCodec codec :: JSONCodec (NonEmpty Int)toJSONVia c (5 :| [])Number 5.0toJSONVia c (5 :| [6])Array [Number 5.0,Number 6.0]JSON.parseMaybe (parseJSONVia c) (Number 5) :: Maybe (NonEmpty Int)Just (5 :| [])JSON.parseMaybe (parseJSONVia c) (Array [Number 5, Number 6]) :: Maybe (NonEmpty Int)Just (5 :| [6])
WARNING
If you use nested lists, for example when the given value codec is also a nonEmptyCodec, you may get in trouble with ambiguities during parsing.
API Note
This is a nonempty version of singleOrListCodec.
Vector codec
Build a codec for vectors of values from a codec for a single value.
Example usage
toJSONVia (vectorCodec codec) (Vector.fromList ['a','b'])Array [String "a",String "b"]
API Note
This is a forward-compatible version of ArrayOfCodec without a name.
vectorCodec = ArrayOfCodec NothingAlternative parsing
parseAlternative Like parseAlternatives, but with only one alternative codec
Example usage
Values
data Fruit = Apple | Orange deriving (Show, Eq, Bounded, Enum)let c = parseAlternative shownBoundedEnumCodec (stringConstCodec [(Apple, "foo"), (Orange, "bar")])toJSONVia c AppleString "Apple"JSON.parseMaybe (parseJSONVia c) (String "foo") :: Maybe FruitJust AppleJSON.parseMaybe (parseJSONVia c) (String "Apple") :: Maybe FruitJust Apple
Required object fields
data Fruit = Apple | Orange deriving (Show, Eq, Bounded, Enum)let c = shownBoundedEnumCodeclet o = parseAlternative (requiredFieldWith "current" c "current key for this field") (requiredFieldWith "legacy" c "legacy key for this field")toJSONObjectVia o ApplefromList [("current",String "Apple")]JSON.parseMaybe (parseJSONObjectVia o) (KM.fromList [("current",String "Apple")]) :: Maybe FruitJust AppleJSON.parseMaybe (parseJSONObjectVia o) (KM.fromList [("legacy",String "Apple")]) :: Maybe FruitJust AppleJSON.parseMaybe (parseJSONObjectVia o) (KM.fromList [("current",String "Tomato")]) :: Maybe FruitNothing
Required object fields
While parseAlternative works exactly like you would expect it would with requiredField, using parseAlterternative with optional fields has some pitfalls.
data Fruit = Apple | Orange deriving (Show, Eq, Bounded, Enum)let c = shownBoundedEnumCodeclet o = parseAlternative (optionalFieldWith "current" c "current key for this field") (optionalFieldWith "legacy" c "legacy key for this field")toJSONObjectVia o (Just Apple)fromList [("current",String "Apple")]toJSONObjectVia o NothingfromList []JSON.parseMaybe (parseJSONObjectVia o) (KM.fromList [("current",String "Apple")]) :: Maybe (Maybe Fruit)Just (Just Apple)
! This is the important result ! The second optionalFieldWith is not tried because the first one _succeeds_ in parsing Nothing
JSON.parseMaybe (parseJSONObjectVia o) (KM.fromList [("legacy",String "Apple")]) :: Maybe (Maybe Fruit)Just Nothing
Here the parser succeeds as well, because it fails to parse the current field, so it tries to parse the legacy field, which is missing.
JSON.parseMaybe (parseJSONObjectVia o) (KM.fromList [("current",String "Tomato")]) :: Maybe (Maybe Fruit)Just Nothing
parseAlternatives Use one codec for the default way of parsing and rendering, but then also use a list of other codecs for potentially different parsing.
You can use this for keeping old ways of parsing intact while already rendering in the new way.
Example usage
data Fruit = Apple | Orange deriving (Show, Eq, Bounded, Enum)let c = parseAlternatives shownBoundedEnumCodec [stringConstCodec [(Apple, "foo"), (Orange, "bar")]]toJSONVia c AppleString "Apple"JSON.parseMaybe (parseJSONVia c) (String "foo") :: Maybe FruitJust AppleJSON.parseMaybe (parseJSONVia c) (String "Apple") :: Maybe FruitJust AppleJSON.parseMaybe (parseJSONVia c) (String "Tomato") :: Maybe FruitNothing
Choice
matchChoiceCodec A choice codec, but unlike eitherCodec, it's for the same output type instead of different ones.
While parsing, this codec will first try the left codec, then the right if that fails.
While rendering, the provided function is used to decide which codec to use for rendering.
Note: The reason this is less primitive than the eitherCodec is that Either makes it clear which codec you want to use for rendering. In this case, we need to provide our own function for choosing which codec we want to use for rendering.
Example usage
:{ let c = matchChoiceCodec (literalTextCodec "even") (literalTextCodec "odd") (\s -> if s == "even" then Left s else Right s):}
toJSONVia c "even"String "even"toJSONVia c "odd"String "odd"JSON.parseMaybe (parseJSONVia c) (String "even") :: Maybe TextJust "even"JSON.parseMaybe (parseJSONVia c) (String "odd") :: Maybe TextJust "odd"
API Note
This is a forward-compatible version of 'matchChoiceCodecAs PossiblyJointUnion':
disjointMatchChoiceCodec = matchChoiceCodecAs PossiblyJointUnionDisjoint version of matchChoiceCodec
API Note
This is a forward-compatible version of 'matchChoiceCodecAs DisjointUnion':
disjointMatchChoiceCodec = matchChoiceCodecAs DisjointUnionmatchChoiceCodecAs An even more general version of matchChoiceCodec and disjointMatchChoiceCodec.
matchChoicesCodec A choice codec for a list of options, each with their own rendering matcher.
During parsing, each of the codecs are tried from first to last until one succeeds.
During rendering, each matching function is tried until either one succeeds and the corresponding codec is used, or none succeed and the fallback codec is used.
Example usage
:{ let c = matchChoicesCodec [ (\s -> if s == "even" then Just s else Nothing, literalTextCodec "even") , (\s -> if s == "odd" then Just s else Nothing, literalTextCodec "odd") ] (literalTextCodec "fallback"):}
toJSONVia c "even"String "even"toJSONVia c "odd"String "odd"toJSONVia c "foobar"String "fallback"JSON.parseMaybe (parseJSONVia c) (String "even") :: Maybe TextJust "even"JSON.parseMaybe (parseJSONVia c) (String "odd") :: Maybe TextJust "odd"JSON.parseMaybe (parseJSONVia c) (String "foobar") :: Maybe TextNothingJSON.parseMaybe (parseJSONVia c) (String "fallback") :: Maybe TextJust "fallback"
API Note
This is a forward-compatible version of 'matchChoicesCodecAs DisjointUnion'.
disjointMatchChoiceCodec = matchChoicesCodecAs DisjointUnionDisjoint version of matchChoicesCodec
API Note
This is a forward-compatible version of 'matchChoicesCodecAs DisjointUnion'.
disjointMatchChoiceCodec = matchChoicesCodecAs DisjointUnionmatchChoicesCodecAs An even more general version of matchChoicesCodec and disjointMatchChoicesCodec
Adding documentation to a codec
Add a comment to a codec
This is an infix version of CommentCodec > (?) = flip CommentCodec
A version of <?> that lets you supply a list of lines of text instead of a single text.
This helps when you use an automated formatter that deals with lists more nicely than with multi-line strings.
Bare codec
5 declarationsA Self-documenting encoder and decoder,
also called an Autodocodec.
In an ideal situation, this type would have only one type parameter: 'Codec value'. This does not work very well because we want to be able to implement Functor and Applicative, which each require a kind '* -> *'. So instead we use two type parameters.
The two type parameters correspond to the phase in which they are used:
The
inputparameter is used for the type that is used during encoding of a value, so it's theinputto the codec.The
outputparameter is used for the type that is used during decoding of a value, so it's theoutputof the codec.Both parameters are unused during documentation.
Constructors
NullCodec :: (Coercible input (), Coercible output ()) => Codec Value input outputEncode
()to thenullvalue, and decodenullas().BoolCodec :: (Coercible input Bool, Coercible output Bool) => Maybe Text -> Codec Value input outputStringCodec :: (Coercible input Text, Coercible output Text) => Maybe Text -> Codec Value input outputIntegerCodec :: (Coercible input Integer, Coercible output Integer) => Maybe Text -> Bounds Integer -> Codec Value input outputEncode Integer to a
numbervalue, and decode anumbervalue as an Integer.The number has 'Bounds Integer'. These are only enforced at decoding time, not at encoding-time.
NOTE: Decoding Integers is dangerous so decoding may fail for enormous numbers. API NOTE: This is separate from NumberCodec so that we can produce more precise documentation about whether the numbers are integers.
NumberCodec :: (Coercible input Scientific, Coercible output Scientific) => Maybe Text -> Bounds Scientific -> Codec Value input outputEncode Scientific to a
numbervalue, and decode anumbervalue as a Scientific.The number has 'Bounds Scientific'. These are only enforced at decoding time, not at encoding-time.
NOTE: We use Scientific here because that is what aeson uses.
HashMapCodec :: (Eq k, Hashable k, FromJSONKey k, ToJSONKey k, Coercible input (HashMap k v), Coercible output (HashMap k v)) => JSONCodec v -> Codec Value input outputMapCodec :: (Ord k, FromJSONKey k, ToJSONKey k, Coercible input (Map k v), Coercible output (Map k v)) => JSONCodec v -> Codec Value input outputValueCodec :: (Coercible Value input, Coercible Value output) => Codec Value input outputArrayOfCodec :: (Coercible input (Vector input1), Coercible output (Vector output1)) => Maybe Text -> ValueCodec input1 output1 -> Codec Value input outputObjectOfCodec :: Maybe Text -> ObjectCodec input output -> Codec Value input outputEncode a value as a an
objectvalue using the given ObjectCodec, and decode anobjectvalue as a value using the given ObjectCodec.EqCodec :: (Show value, Eq value, Coercible input value, Coercible output value) => value -> JSONCodec value -> Codec Value input outputMatch a given value using its Eq instance during decoding, and encode exactly that value during encoding.
BimapCodec :: (oldOutput -> Either String output) -> (input -> oldInput) -> Codec context oldInput oldOutput -> Codec context input outputMap a codec in both directions.
This is not strictly dimap, because the decoding function is allowed to fail, but we can implement dimap using this function by using a decoding function that does not fail. Otherwise we would have to have another constructor here.
EitherCodec :: (Coercible input (Either input1 input2), Coercible output (Either output1 output2)) => !Union -> Codec context input1 output1 -> Codec context input2 output2 -> Codec context input outputEncode/Decode an Either value
During encoding, encode either value of an Either using their own codec. During decoding, try to parse the Left side first, and the Right side only when that fails.
This codec is used to implement choice.
Note that this codec works for both values and objects. However: due to the complex nature of documentation, the documentation may not be as good as you would hope when you use this codec. In particular, you should prefer using it for values rather than objects, because those docs are easier to generate.
DiscriminatedUnionCodec :: Text -> (input -> (Discriminator, ObjectCodec input ())) -> HashMap Discriminator (Text, ObjectCodec Void output) -> Codec (KeyMap Value) input outputEncode/decode a discriminated union of objects
The type of object being encoded/decoded is discriminated by a designated "discriminator" property on the object which takes a string value.
When encoding, the provided function is applied to the input to obtain a new encoder for the input. The function mapToEncoder is provided to assist with building these encoders.
When decoding, the value of the discriminator property is looked up in the HashMap to obtain a decoder for the output. The function
mapToDecoderis provided to assist with building these decoders. See examples inUsage.hs.The HashMap is also used to generate schemas for the type. In particular, for OpenAPI 3, it will generate a schema with a
discriminator, as defined by https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/CommentCodec :: Text -> ValueCodec input output -> Codec Value input outputA comment codec
This is used to add implementation-irrelevant but human-relevant information.
ReferenceCodec :: Text -> ~ValueCodec input output -> Codec Value input outputA reference codec
This is used for naming a codec, so that recursive codecs can have a finite schema.
It doesn't _need_ to be recursive, and you may just have wanted to name the codec, but it _may_ be recursive from here downward.
This value MUST be lazy, otherwise we can never define recursive codecs.
RequiredKeyCodec :: (Coercible input input1, Coercible output output1) => Text -> ValueCodec input1 output1 -> Maybe Text -> Codec (KeyMap Value) input outputOptionalKeyCodec :: (Coercible input (Maybe input1), Coercible output (Maybe output1)) => Text -> ValueCodec input1 output1 -> Maybe Text -> Codec (KeyMap Value) input outputOptionalKeyWithDefaultCodec :: Coercible output input => Text -> ValueCodec input input -> input -> Maybe Text -> Codec (KeyMap Value) input outputOptionalKeyWithOmittedDefaultCodec :: (Eq value, Coercible input value, Coercible output value) => Text -> ValueCodec value value -> value -> Maybe Text -> Codec (KeyMap Value) input outputPureCodec :: output -> Codec (KeyMap Value) input outputTo implement pure from Applicative.
Pure is not available for non-object codecs because there is no mempty for Value, which we would need during encoding.
ApCodec :: ObjectCodec input (output1 -> output) -> ObjectCodec input output1 -> Codec (KeyMap Value) input outputTo implement <*> from Applicative.
Ap is not available for non-object codecs because we cannot combine (mappend) two encoded Values
Instances2Applicative, Functor
Applicative (ObjectCodec input)Defined in autodocodec-0.5.0.0 · Autodocodec.CodecFunctor (Codec context input)Defined in autodocodec-0.5.0.0 · Autodocodec.Codec
A codec within the Value context.
An ValueCodec can be used to turn a Haskell value into a Value or to parse a Value into a haskell value.
This cannot be used in certain places where ObjectCodec could be used, and vice versa.
A codec within the Object context.
An Object can be used to turn a Haskell value into a Object or to parse a Object into a haskell value.
This cannot be used in certain places where ValueCodec could be used, and vice versa.
Produce a value without parsing any part of an Object.
This function exists to implement Applicative (ObjectCodec input).
API Note
This is a forward-compatible version of PureCodec.
pureCodec = PureCodecSequentially apply two codecs that parse part of an Object.
This function exists to implement Applicative (ObjectCodec input).
API Note
This is a forward-compatible version of ApCodec.
apCodec = ApCodecDeriving Via
1 declarationAutodocodec is a wrapper to provide codec-based deriving strategies.
Example usage
data Via = Via {viaOne :: !Text, viaTwo :: !Text}
deriving stock (Show, Eq, Generic)
deriving (FromJSON, ToJSON) via (Autodocodec Via)
instance HasCodec Via where
codec =
object "Via" $
Via
<$> requiredField "one" "first field" .= viaOne
<*> requiredField "two" "second field" .= viaTwoConstructors
Instances2FromJSON, ToJSON
HasCodec a => FromJSON (Autodocodec a)Defined in autodocodec-0.5.0.0 · Autodocodec.DerivingViaHasCodec a => ToJSON (Autodocodec a)Defined in autodocodec-0.5.0.0 · Autodocodec.DerivingVia
Internals you most likely don't need
Show a codec to a human.
This function exists for codec debugging. It omits any unshowable information from the output.
To make sure we definitely export everything
0 declarationsmodule Autodocodec.Aeson
module Autodocodec.Class
module Autodocodec.DerivingVia
module Autodocodec.Codec