| Copyright | (C) 2016-2026 David M. Johnson |
|---|---|
| License | BSD3-style (see the file LICENSE) |
| Maintainer | David M. Johnson <code@dmj.io> |
| Stability | experimental |
| Portability | non-portable |
| Safe Haskell | None |
| Language | Haskell2010 |
Miso.JSON
Description
Overview
Miso.JSON is a JSON library tailored to MisoString, modelled after
aeson and inspired by
microaeson. It provides
encoding, decoding, and a Generic-deriving mechanism that mirrors aeson's
defaults, making it straightforward to reuse existing aeson-compatible type
class instances.
Platform behaviour
- Client (WASM / GHC JS backend) —
encodecallsJSON.stringify()anddecodecallsJSON.parse()via FFI for maximum performance. - Server (
-fssr/1build) — a pure Haskell lexer/parser pipeline (Miso.JSON.Lexer + Miso.JSON.Parser) is used instead, with no JavaScript dependency.
The same type class instances work on both platforms; only the underlying serialisation primitive differs.
Quick start
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic)
import Miso.JSON
import Miso.String (MisoString)
data Person = Person
{ name :: MisoString
, age :: Int
} deriving (Generic, Show, Eq)
instance ToJSON Person
instance FromJSON Person
-- Encode to a JSON string:
encoded :: MisoString
encoded = encode (Person "Alice" 30)
-- Result: "{\"name\":\"Alice\",\"age\":30}"
-- Decode from a JSON string:
decoded :: Maybe Person
decoded = decode encoded
Constructing JSON values
Use object and .= to build Value trees without defining a type:
point ::Valuepoint =object[ "x".=(10 :: Int), "y".=(20 :: Int) ]
Writing instances by hand
data Color = Red | Green | Blue instanceToJSONColor wheretoJSONRed =String"red"toJSONGreen =String"green"toJSONBlue =String"blue" instanceFromJSONColor whereparseJSON=withText"Color" $ \case "red" -> pure Red "green" -> pure Green "blue" -> pure Blue t ->typeMismatch"Color" (Stringt)
Generic encoding options
Generic instances follow aeson's default strategy. Customise with Options:
myOptions ::OptionsmyOptions =defaultOptions{fieldLabelModifier=camelTo2'_' } instanceToJSONPerson wheretoJSON=genericToJSONmyOptions -- { "person_name": "Alice", "person_age": 30 }
API groups
- Core types —
Value,Object,Pair,Result - Constructors —
object,.=,emptyObject,emptyArray - Accessors —
.:(required),.:?(optional),.:!(optional/nullable),.!=(default) - Encoding —
encode,encodePure,encodePretty,encodePretty' - Decoding —
decode,eitherDecode,decodePure - Type classes —
ToJSON,FromJSON,Parser - Prism-style parsers —
withObject,withText,withArray,withNumber,withBool - Conversion —
fromJSON,parseMaybe,parseEither - Options / Generics —
Options,defaultOptions,genericToJSON,genericParseJSON,camelTo2 - FFI —
toJSVal_Value,fromJSVal_Value,jsonStringify,jsonParse
See also
- Miso.JSON.Types —
ValueandResulttype definitions - Miso.JSON.Lexer — pure Haskell JSON tokeniser (server build)
- Miso.JSON.Parser — pure Haskell JSON parser (server build)
- Miso.Event.Decoder — uses
ParserandValuefor DOM event decoding - Miso.String —
MisoString,ms
Synopsis
- data Value
- type Object = Map MisoString Value
- type Pair = (MisoString, Value)
- data Result a
- = Success a
- | Error MisoString
- (.=) :: ToJSON v => MisoString -> v -> Pair
- object :: [Pair] -> Value
- emptyArray :: Value
- emptyObject :: Value
- (.:) :: FromJSON a => Object -> MisoString -> Parser a
- (.:?) :: FromJSON a => Object -> MisoString -> Parser (Maybe a)
- (.:!) :: FromJSON a => Object -> MisoString -> Parser (Maybe a)
- (.!=) :: Parser (Maybe a) -> a -> Parser a
- encode :: ToJSON a => a -> MisoString
- encodePure :: ToJSON a => a -> MisoString
- decode :: FromJSON a => MisoString -> Maybe a
- decodePure :: MisoString -> Either String Value
- withObject :: MisoString -> (Object -> Parser a) -> Value -> Parser a
- withText :: MisoString -> (MisoString -> Parser a) -> Value -> Parser a
- withArray :: MisoString -> ([Value] -> Parser a) -> Value -> Parser a
- withNumber :: MisoString -> (Double -> Parser a) -> Value -> Parser a
- withBool :: MisoString -> (Bool -> Parser a) -> Value -> Parser a
- class FromJSON a where
- newtype Parser a = Parser {
- unParser :: Either MisoString a
- parseMaybe :: (a -> Parser b) -> a -> Maybe b
- class ToJSON a where
- fromJSON :: FromJSON a => Value -> Result a
- parseEither :: (a -> Parser b) -> a -> Either MisoString b
- eitherDecode :: FromJSON a => MisoString -> Either MisoString a
- typeMismatch :: MisoString -> Value -> Parser a
- encodePretty :: ToJSON a => a -> MisoString
- encodePretty' :: ToJSON a => Config -> a -> MisoString
- defConfig :: Config
- newtype Config = Config {}
- fromJSVal_Value :: JSVal -> IO (Maybe Value)
- toJSVal_Value :: Value -> IO JSVal
- jsonStringify :: JSVal -> IO MisoString
- jsonParse :: MisoString -> IO JSVal
- data Options = Options {}
- defaultOptions :: Options
- class GToJSON (f :: Type -> Type) where
- class GToFields (f :: Type -> Type) where
- class GToJSONSum (f :: Type -> Type) where
- gToJSONSum :: Options -> f a -> Value
- class GAllNullary (f :: Type -> Type) where
- gAllNullary :: Bool
- data Fields
- = RecordFields [(MisoString, Value)]
- | PositionalFields [Value]
- class GFromJSON (f :: Type -> Type) where
- gParseJSON :: Options -> Value -> Parser (f a)
- class GFromFields (f :: Type -> Type) where
- gIsRecord :: Bool
- gFieldCount :: Int
- gFromRecord :: Options -> Object -> Parser (f a)
- gFromPositional :: Options -> [Value] -> Parser (f a)
- class GFromJSONSum (f :: Type -> Type) where
- gFromJSONSum :: Options -> Value -> Parser (f a)
- genericToJSON :: (Generic a, GToJSON (Rep a)) => Options -> a -> Value
- genericParseJSON :: (Generic a, GFromJSON (Rep a)) => Options -> Value -> Parser a
- camelTo2 :: Char -> String -> String
JSON
Core JSON types
Constructors
| Number Double | |
| Bool Bool | |
| String MisoString | |
| Array [Value] | |
| Object (Map MisoString Value) | |
| Null |
Instances
| IsString Value Source # | |
Defined in Miso.JSON.Types Methods fromString :: String -> Value # | |
| Show Value Source # | |
| Eq Value Source # | |
| FromJSVal Value Source # | |
| ToJSVal Value Source # | |
| FromJSON Value Source # | |
| ToJSON Value Source # | |
| FromMisoString Value Source # | |
Defined in Miso.JSON Methods fromMisoStringEither :: MisoString -> Either String Value Source # | |
| ToMisoString Value Source # | |
Defined in Miso.JSON Methods toMisoString :: Value -> MisoString Source # | |
type Pair = (MisoString, Value) Source #
Constructors
| Success a | |
| Error MisoString |
Instances
| Alternative Result Source # | |
| Applicative Result Source # | |
| Functor Result Source # | |
| Monad Result Source # | |
| MonadPlus Result Source # | |
| MonadFail Result Source # | |
Defined in Miso.JSON.Types | |
| Foldable Result Source # | |
Defined in Miso.JSON.Types Methods fold :: Monoid m => Result m -> m # foldMap :: Monoid m => (a -> m) -> Result a -> m # foldMap' :: Monoid m => (a -> m) -> Result a -> m # foldr :: (a -> b -> b) -> b -> Result a -> b # foldr' :: (a -> b -> b) -> b -> Result a -> b # foldl :: (b -> a -> b) -> b -> Result a -> b # foldl' :: (b -> a -> b) -> b -> Result a -> b # foldr1 :: (a -> a -> a) -> Result a -> a # foldl1 :: (a -> a -> a) -> Result a -> a # elem :: Eq a => a -> Result a -> Bool # maximum :: Ord a => Result a -> a # minimum :: Ord a => Result a -> a # | |
| Traversable Result Source # | |
| Monoid (Result a) Source # | |
| Semigroup (Result a) Source # | |
| Show a => Show (Result a) Source # | |
| Eq a => Eq (Result a) Source # | |
Constructors
emptyArray :: Value Source #
The empty JSON Array (i.e. []).
emptyObject :: Value Source #
The empty JSON Object (i.e. {}).
Accessors
(.:) :: FromJSON a => Object -> MisoString -> Parser a Source #
Look up a required key in a JSON Object.
Fails with a parse error if the key is absent.
Encoding and decoding
encode :: ToJSON a => a -> MisoString Source #
Encode a value as a JSON MisoString.
On the client (WASM / GHC JS backend) calls JSON.stringify() via FFI for
maximum performance. On the server (1) falls back to encodePure.
encodePure :: ToJSON a => a -> MisoString Source #
Relies on the pure implementation of JSON parsing / serialization.
This can be used on the server or the client, it is more efficient to
use encode on the client (since it relies on JSON.stringify()).
decode :: FromJSON a => MisoString -> Maybe a Source #
Decode a JSON MisoString into a Haskell value, returning Nothing on failure.
On the client calls JSON.parse() via FFI. On the server uses the pure
Haskell parser. For a human-readable error message on failure use eitherDecode.
decodePure :: MisoString -> Either String Value Source #
Prism-style parsers
Arguments
| :: MisoString | Expected type name used in the error message |
| -> (Object -> Parser a) | Continuation receiving the |
| -> Value | JSON value to inspect |
| -> Parser a |
Succeed only when the Value is a JSON object; fail with typeMismatch otherwise.
The inner parser receives the Object map for key lookups with .: etc.
Arguments
| :: MisoString | Expected type name used in the error message |
| -> (MisoString -> Parser a) | Continuation receiving the unwrapped string |
| -> Value | JSON value to inspect |
| -> Parser a |
Succeed only when the Value is a JSON string (MisoString);
fail with typeMismatch otherwise.
Arguments
| :: MisoString | Expected type name used in the error message |
| -> ([Value] -> Parser a) | Continuation receiving the list of array elements |
| -> Value | JSON value to inspect |
| -> Parser a |
Succeed only when the Value is a JSON array; fail with typeMismatch otherwise.
The inner parser receives the list of elements.
Arguments
| :: MisoString | Expected type name used in the error message |
| -> (Double -> Parser a) | Continuation receiving the numeric value as a |
| -> Value | JSON value to inspect |
| -> Parser a |
Succeed only when the Value is a JSON number; fail with typeMismatch otherwise.
The inner parser receives the underlying Double.
Arguments
| :: MisoString | Expected type name used in the error message (e.g. |
| -> (Bool -> Parser a) | Continuation receiving the unwrapped |
| -> Value | JSON value to inspect |
| -> Parser a |
Succeed only when the Value is a Bool; fail with typeMismatch otherwise.
Type conversion
class FromJSON a where Source #
A type that can be deserialised from a JSON Value.
Instances for the most common Haskell types are provided. Derive via
Generics for product/sum types, or write instances by hand:
-- Generic derivation:
data Point = Point { x :: Double, y :: Double }
deriving (Generic, Show, Eq)
instance FromJSON Point
-- Manual instance:
instance FromJSON Point where
parseJSON = withObject "Point" $ \o ->
Point <$> o .: "x" <*> o .: "y"
Minimal complete definition
Nothing
Methods
Instances
| FromJSON Int16 Source # | |
| FromJSON Int32 Source # | |
| FromJSON Int64 Source # | |
| FromJSON Int8 Source # | |
| FromJSON Word16 Source # | |
| FromJSON Word32 Source # | |
| FromJSON Word64 Source # | |
| FromJSON Word8 Source # | |
| FromJSON Ordering Source # | |
| FromJSON Checked Source # | |
| FromJSON KeyCode Source # | |
| FromJSON PointerType Source # | |
Defined in Miso.Event.Types | |
| FromJSON Value Source # | |
| FromJSON MisoString Source # | |
| FromJSON URI Source # | |
| FromJSON Text Source # | |
| FromJSON Integer Source # | |
| FromJSON Natural Source # | |
| FromJSON String Source # | |
| FromJSON () Source # | |
| FromJSON Bool Source # | |
| FromJSON Char Source # | |
| FromJSON Double Source # | |
| FromJSON Float Source # | |
| FromJSON Int Source # | |
| FromJSON Word Source # | |
| FromJSON a => FromJSON (Maybe a) Source # | |
| FromJSON a => FromJSON [a] Source # | |
| FromJSON v => FromJSON (Map MisoString v) Source # | |
| (FromJSON a, FromJSON b) => FromJSON (a, b) Source # | |
| (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a, b, c) Source # | |
| (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON (a, b, c, d) Source # | |
A lightweight JSON parse monad. Wraps Either MisoString a so that
parse failures carry a human-readable error message.
Parser is a Functor, Applicative, Monad, MonadFail, and
Alternative. The Alternative instance tries the right branch only when
the left branch fails — useful for decoding sum types with multiple valid
shapes.
Combine with parseJSON and the accessor operators (.: etc.) to build
composite decoders:
data Point = Point Double Double instanceFromJSONPoint whereparseJSON=withArray"Point" $ \xs -> Point <$>parseJSON(xs!!0) <*>parseJSON(xs!!1)
Constructors
| Parser | |
Fields
| |
Run a parser function, returning Nothing on failure instead of an error string.
A type that can be serialised to a JSON Value.
Instances for the most common Haskell types are provided. Derive via
Generics for product/sum types, or write instances by hand for
full control:
-- Generic derivation (mirrors aeson defaults):
data Point = Point { x :: Double, y :: Double }
deriving (Generic, Show, Eq)
instance ToJSON Point
-- Manual instance:
instance ToJSON Point where
toJSON (Point x y) = object ["x" .= x, "y" .= y]
Minimal complete definition
Nothing
Methods
Convert a value to a JSON Value.
Instances
Misc.
Arguments
| :: (a -> Parser b) | Parser function to apply |
| -> a | Input value to parse |
| -> Either MisoString b |
Run a parser function, returning on failure.Left errMsg
eitherDecode :: FromJSON a => MisoString -> Either MisoString a Source #
Decode a JSON MisoString, returning on failure.
Prefer Left errMsgdecode when the error message is not needed.
Arguments
| :: MisoString | Human-readable name of the expected type (e.g. |
| -> Value | The actual |
| -> Parser a |
Produce a parse failure describing a type mismatch.
Used by the with* combinators and useful in hand-written FromJSON instances.
Pretty
encodePretty :: ToJSON a => a -> MisoString Source #
Encode a value as indented (pretty-printed) JSON using defConfig
(4-space indentation). Not available in the 1 build.
encodePretty' :: ToJSON a => Config -> a -> MisoString Source #
Like encodePretty but with a custom Config.
Not available in the 1 build.
Pretty-printing configuration for encodePretty and encodePretty'.
FFI
jsonStringify :: JSVal -> IO MisoString Source #
Call JSON.stringify() on a JavaScript value, returning a JSON string.
jsonParse :: MisoString -> IO JSVal Source #
Call JSON.parse() on a JSON string, returning a raw JavaScript value.
Options
Configuration for generic JSON encoding and decoding via genericToJSON
and genericParseJSON. Mirrors the subset of aeson's Options that is
relevant to miso's Generic machinery.
Construct with defaultOptions and override only the fields you need:
myOpts ::OptionsmyOpts =defaultOptions{fieldLabelModifier=camelTo2'_' }
Constructors
| Options | |
Fields
| |
defaultOptions :: Options Source #
Default encoding/decoding options, matching aeson's defaults:
no field or constructor name transformation, allNullaryToStringTag enabled,
omitNothingFields disabled.
Generics
class GToJSON (f :: Type -> Type) where Source #
Top-level generic encoding class.
Encoding rules match aeson's defaults:
- All-nullary sum +
allNullaryToStringTag:"C" - Single-constructor record:
{"field1": v1, ...} - Single-constructor positional:
v(1 field),[v1,v2,...](n>1),[](0) - Sum record constructor:
{"tag": "C", "field1": v1, ...} - Sum nullary constructor:
{"tag": "C"} - Sum positional constructor:
{"tag": "C", "contents": v}or[...]
class GToFields (f :: Type -> Type) where Source #
Collect a constructor's fields into Fields.
Instances
| GToFields (U1 :: Type -> Type) Source # | |
| GToFields (V1 :: Type -> Type) Source # | |
| (GToFields f, GToFields g) => GToFields (f :*: g) Source # | |
| ToJSON a => GToFields (K1 r a :: Type -> Type) Source # | |
| (Selector m, ToJSON a) => GToFields (S1 m (K1 r (Maybe a) :: Type -> Type)) Source # | Special |
| (Selector m, GToFields f) => GToFields (S1 m f) Source # | |
class GToJSONSum (f :: Type -> Type) where Source #
Encode sum constructors with a "tag" key.
Methods
gToJSONSum :: Options -> f a -> Value Source #
Instances
| (GToJSONSum f, GToJSONSum g) => GToJSONSum (f :+: g) Source # | |
| (Constructor m, GToFields f) => GToJSONSum (C1 m f) Source # | |
class GAllNullary (f :: Type -> Type) where Source #
Determine at the type level whether every constructor of a sum type
is nullary (has no fields). Used to implement allNullaryToStringTag.
Methods
gAllNullary :: Bool Source #
Instances
| GAllNullary (U1 :: Type -> Type) Source # | |
Defined in Miso.JSON Methods gAllNullary :: Bool Source # | |
| (GAllNullary f, GAllNullary g) => GAllNullary (f :*: g) Source # | |
Defined in Miso.JSON Methods gAllNullary :: Bool Source # | |
| (GAllNullary f, GAllNullary g) => GAllNullary (f :+: g) Source # | |
Defined in Miso.JSON Methods gAllNullary :: Bool Source # | |
| GAllNullary f => GAllNullary (C1 m f) Source # | |
Defined in Miso.JSON Methods gAllNullary :: Bool Source # | |
| GAllNullary (K1 r a :: Type -> Type) Source # | |
Defined in Miso.JSON Methods gAllNullary :: Bool Source # | |
| GAllNullary f => GAllNullary (S1 m f) Source # | |
Defined in Miso.JSON Methods gAllNullary :: Bool Source # | |
Intermediate representation of a constructor's fields after encoding.
RecordFields is produced when every selector has a name (record syntax);
PositionalFields is produced for all other constructors.
Constructors
| RecordFields [(MisoString, Value)] | Named fields (record constructor) |
| PositionalFields [Value] | Positional fields (non-record constructor) |
class GFromJSON (f :: Type -> Type) where Source #
Top-level generic decoding class. Symmetric with GToJSON.
Decoding rules match aeson's defaults (see Options and defaultOptions).
class GFromFields (f :: Type -> Type) where Source #
Field-level decoder. Knows whether the constructor is a record and
how many fields it has; can decode from a JSON Object (record mode)
or a positional '[Value]' list.
Methods
Is this a record constructor (all selectors have names)?
gFieldCount :: Int Source #
Number of fields.
gFromRecord :: Options -> Object -> Parser (f a) Source #
Decode from a JSON Object (record mode: look up by field name).
gFromPositional :: Options -> [Value] -> Parser (f a) Source #
Decode from a positional list of Value.
Instances
| GFromFields (U1 :: Type -> Type) Source # | |
| GFromFields (V1 :: Type -> Type) Source # | |
| (GFromFields f, GFromFields g) => GFromFields (f :*: g) Source # | |
| (Selector m, FromJSON a) => GFromFields (S1 m (K1 r (Maybe a) :: Type -> Type)) Source # | Selector with a |
| (Selector m, FromJSON a) => GFromFields (S1 m (K1 r a :: Type -> Type)) Source # | General selector. |
class GFromJSONSum (f :: Type -> Type) where Source #
Parse sum constructors, trying each branch left-to-right.
Instances
| (GFromJSONSum f, GFromJSONSum g) => GFromJSONSum (f :+: g) Source # | |
| (Constructor m, GFromFields f) => GFromJSONSum (C1 m f) Source # | |
Arguments
| :: (Generic a, GToJSON (Rep a)) | |
| => Options | Encoding options (field/constructor name modifiers, etc.) |
| -> a | Value to encode |
| -> Value |
Derive toJSON via Generics with custom Options.
Called by the default ToJSON implementation using defaultOptions.
Arguments
| :: (Generic a, GFromJSON (Rep a)) | |
| => Options | Decoding options (field/constructor name modifiers, etc.) |
| -> Value | JSON |
| -> Parser a |
Derive parseJSON via Generics with custom Options.
Called by the default FromJSON implementation using defaultOptions.
Modifiers
Arguments
| :: Char | Delimiter character to insert between words (e.g. |
| -> String | camelCase identifier to transform |
| -> String |
Convert a camelCase identifier to a separated form using the given delimiter.
camelTo2 '_' "camelCaseField" == "camel_case_field"
Commonly used as fieldLabelModifier in a custom Options.
Orphan instances
| FromMisoString Value Source # | |
Methods fromMisoStringEither :: MisoString -> Either String Value Source # | |
| ToMisoString Value Source # | |
Methods toMisoString :: Value -> MisoString Source # | |